Cantor set

From Rosetta Code
Cantor set is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Draw Cantor set. See details: Cantor set


ALGOL 68[edit]

BEGIN
# draw a Cantor Set using ASCII #
INT lines = 5; # number of lines for the set #
# we must choose the line width so that the width of each segment is #
# divisible by 3 ( except for the final line where the segment width will #
# be 1 ) #
INT set width = 3 ^ ( lines - 1 );
[ set width ]CHAR set;
# start with a complete line #
FOR i TO set width DO set[ i ] := "#" OD;
print( ( set, newline ) );
# repeatedly modify the line, replacing the middle third of each segment #
# with blanks #
INT segment width := set width OVER 3;
WHILE segment width > 0 DO
INT set pos := 1;
WHILE set pos < ( set width - segment width ) DO
set pos +:= segment width;
FOR char pos FROM set pos TO ( set pos + segment width ) - 1 DO
set[ char pos ] := " "
OD;
set pos +:= segment width
OD;
print( ( set, newline ) );
segment width OVERAB 3
OD
END
Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

ALGOL W[edit]

Based on the Algol 68 sample.

begin
 % draw a Cantor Set using ASCII  %
integer LINES;  % number of lines for the set  %
integer setWidth;  % width of each line of the set  %
 % we must choose the line width so that the width of each segment is  %
 % divisible by 3 ( except for the final line where the segment width will  %
 % be 1 )  %
LINES  := 5;
setWidth := round( 3 ** ( LINES - 1 ) );
begin % start new block so the array can have computed bounds  %
logical array set ( 1 :: setWidth );
integer segmentWidth;
 % start with a complete line %
for i := 1 until setWidth do set( i ) := true;
segmentWidth := setWidth;
for l := 1 until LINES do begin
 % print the latest line, all lines start with a "#"  %
write( "#" );
for i := 2 until setWidth do writeon( if set( i ) then "#" else " " );
 % modify the line, replacing the middle third of each segment  %
 % with blanks, unless this was the last line  %
if l < LINES then begin
integer setPos;
segmentWidth := segmentWidth div 3;
setPos := 1;
while setPos < ( setWidth - segmentWidth ) do begin
setPos := setPos + segmentWidth;
for charPos := setPos until ( setPos + segmentWidth ) - 1 do set( charPos ) := false;
setPos := setPos + segmentWidth
end while_setPos_in_range ;
end if_l_lt_LINES
end for_l
end
end.
Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

AWK[edit]

 
# syntax: GAWK -f CANTOR_SET.AWK
# converted from C
BEGIN {
WIDTH = 81
HEIGHT = 5
for (i=0; i<HEIGHT; ++i) {
for (j=0; j<WIDTH; ++j) {
lines[i][j] = "*"
}
}
cantor(0,WIDTH,1)
for (i=0; i<HEIGHT; ++i) {
for (j=0; j<WIDTH; ++j) {
printf("%s",lines[i][j])
}
printf("\n")
}
exit(0)
}
function cantor(start,leng,indx, i,j,seg) {
seg = int(leng/3)
if (seg == 0) { return }
for (i=indx; i<HEIGHT; ++i) {
for (j=start+seg; j<start+seg*2; ++j) {
lines[i][j] = " "
}
}
cantor(start,seg,indx+1)
cantor(start+seg*2,seg,indx+1)
}
 
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

C[edit]

Translation of: Kotlin
#include <stdio.h>
 
#define WIDTH 81
#define HEIGHT 5
 
char lines[HEIGHT][WIDTH];
 
void init() {
int i, j;
for (i = 0; i < HEIGHT; ++i) {
for (j = 0; j < WIDTH; ++j) lines[i][j] = '*';
}
}
 
void cantor(int start, int len, int index) {
int i, j, seg = len / 3;
if (seg == 0) return;
for (i = index; i < HEIGHT; ++i) {
for (j = start + seg; j < start + seg * 2; ++j) lines[i][j] = ' ';
}
cantor(start, seg, index + 1);
cantor(start + seg * 2, seg, index + 1);
}
 
void print() {
int i, j;
for (i = 0; i < HEIGHT; ++i) {
for (j = 0; j < WIDTH; ++j) printf("%c", lines[i][j]);
printf("\n");
}
}
 
int main() {
init();
cantor(0, WIDTH, 1);
print();
return 0;
}
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

C#[edit]

Translation of: Java
using System;
 
namespace CantorSet {
class Program {
const int WIDTH = 81;
const int HEIGHT = 5;
private static char[,] lines = new char[HEIGHT, WIDTH];
 
static Program() {
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
lines[i, j] = '*';
}
}
}
 
private static void Cantor(int start, int len, int index) {
int seg = len / 3;
if (seg == 0) return;
for (int i = index; i < HEIGHT; i++) {
for (int j = start + seg; j < start + seg * 2; j++) {
lines[i, j] = ' ';
}
}
Cantor(start, seg, index + 1);
Cantor(start + seg * 2, seg, index + 1);
}
 
static void Main(string[] args) {
Cantor(0, WIDTH, 1);
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
Console.Write(lines[i,j]);
}
Console.WriteLine();
}
}
}
}
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

D[edit]

Translation of: C
import std.stdio;
 
enum WIDTH = 81;
enum HEIGHT = 5;
 
char[WIDTH*HEIGHT] lines;
 
void cantor(int start, int len, int index) {
int seg = len / 3;
if (seg == 0) return;
for (int i=index; i<HEIGHT; i++) {
for (int j=start+seg; j<start+seg*2; j++) {
int pos = i*WIDTH + j;
lines[pos] = ' ';
}
}
cantor(start, seg, index+1);
cantor(start+seg*2, seg, index+1);
}
 
void main() {
// init
lines[] = '*';
 
// calculate
cantor(0, WIDTH, 1);
 
// print
for (int i=0; i<HEIGHT; i++) {
int beg = WIDTH * i;
writeln(lines[beg..beg+WIDTH]);
}
}
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Factor[edit]

USING: grouping.extras io kernel math sequences
sequences.repeating ;
IN: rosetta-code.cantor-set
 
CONSTANT: width 81
CONSTANT: depth 5
 
: cantor ( n -- seq )
dup 0 = [ drop { 0 1 } ]
[ 1 - cantor [ 3 / ] map dup [ 2/3 + ] map append ] if ;
 
! Produces a sequence of lengths from a Cantor set, depending on
! width. Even indices are solid; odd indices are blank.
! e.g. 2 cantor gaps -> { 9 9 9 27 9 9 9 }
!
: gaps ( seq -- seq )
[ width * ] map [ - abs ] 2clump-map ;
 
: print-cantor ( n -- )
cantor gaps [ even? "#" " " ? swap repeat ] map-index
concat print ;
 
depth <iota> [ print-cantor ] each
Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

Go[edit]

Translation of: Kotlin
package main
 
import "fmt"
 
const (
width = 81
height = 5
)
 
var lines [height][width]byte
 
func init() {
for i := 0; i < height; i++ {
for j := 0; j < width; j++ {
lines[i][j] = '*'
}
}
}
 
func cantor(start, len, index int) {
seg := len / 3
if seg == 0 {
return
}
for i := index; i < height; i++ {
for j := start + seg; j < start + 2 * seg; j++ {
lines[i][j] = ' '
}
}
cantor(start, seg, index + 1)
cantor(start + seg * 2, seg, index + 1)
}
 
func main() {
cantor(0, width, 1)
for _, line := range lines {
fmt.Println(string(line[:]))
}
}
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Java[edit]

Translation of: Kotlin
public class App {
private static final int WIDTH = 81;
private static final int HEIGHT = 5;
 
private static char[][] lines;
static {
lines = new char[HEIGHT][WIDTH];
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
lines[i][j] = '*';
}
}
}
 
private static void cantor(int start, int len, int index) {
int seg = len / 3;
if (seg == 0) return;
for (int i = index; i < HEIGHT; i++) {
for (int j = start + seg; j < start + seg * 2; j++) {
lines[i][j] = ' ';
}
}
cantor(start, seg, index + 1);
cantor(start + seg * 2, seg, index + 1);
}
 
public static void main(String[] args) {
cantor(0, WIDTH, 1);
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j < WIDTH; j++) {
System.out.print(lines[i][j]);
}
System.out.println();
}
}
}
 
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Kotlin[edit]

Simple terminal drawing.

// Version 1.2.31
 
const val WIDTH = 81
const val HEIGHT = 5
 
val lines = List(HEIGHT) { CharArray(WIDTH) { '*' } }
 
fun cantor(start: Int, len: Int, index: Int) {
val seg = len / 3
if (seg == 0) return
for (i in index until HEIGHT) {
for (j in start + seg until start + seg * 2) lines[i][j] = ' '
}
cantor(start, seg, index + 1)
cantor(start + seg * 2, seg, index + 1)
}
 
fun main(args: Array<String>) {
cantor(0, WIDTH, 1)
lines.forEach { println(it) }
}
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Modula-2[edit]

Translation of: Kotlin
MODULE Cantor;
FROM Terminal IMPORT Write,WriteLn,ReadChar;
 
CONST
WIDTH = 81;
HEIGHT = 5;
VAR
lines : ARRAY[0..HEIGHT] OF ARRAY[0..WIDTH] OF CHAR;
 
PROCEDURE Init;
VAR i,j : CARDINAL;
BEGIN
FOR i:=0 TO HEIGHT DO
FOR j:=0 TO WIDTH DO
lines[i,j] := '*'
END
END
END Init;
 
PROCEDURE Cantor(start,len,index : CARDINAL);
VAR i,j,seg : CARDINAL;
BEGIN
seg := len DIV 3;
IF seg=0 THEN RETURN END;
FOR i:=index TO HEIGHT-1 DO
j := start+seg;
FOR j:=start+seg TO start+seg*2-1 DO
lines[i,j] := ' '
END
END;
Cantor(start, seg, index+1);
Cantor(start+seg*2, seg, index+1)
END Cantor;
 
PROCEDURE Print;
VAR i,j : CARDINAL;
BEGIN
FOR i:=0 TO HEIGHT-1 DO
FOR j:=0 TO WIDTH-1 DO
Write(lines[i,j])
END;
WriteLn
END
END Print;
 
BEGIN
Init;
Cantor(0,WIDTH,1);
Print;
 
ReadChar;
END Cantor.

Perl[edit]

Translation of: Perl 6
use feature 'say';
 
sub cantor {
my($height) = @_;
my $width = 3 ** ($height - 1);
 
my @lines = ('#' x $width) x $height;
 
sub trim_middle_third {
my($len, $start, $index) = @_;
my $seg = int $len / 3
or return;
 
for $i ( $index .. $height - 1 ) {
for $j ( 0 .. $seg - 1 ) {
substr @lines[$i], $start + $seg + $j, 1, ' ';
}
}
 
trim_middle_third( $seg, $start + $_, $index + 1 ) for 0, $seg * 2;
}
 
trim_middle_third( $width, 0, 1 );
@lines;
}
 
say for cantor(5);
Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

Perl 6[edit]

Translation of: Kotlin
sub cantor ( Int $height ) {
my $width = 3 ** ($height - 1);
 
my @lines = ( "\c[FULL BLOCK]" x $width ) xx $height;
 
my sub _trim_middle_third ( $len, $start, $index ) {
my $seg = $len div 3
or return;
 
for ( $index ..^ $height ) X ( 0 ..^ $seg ) -> ( $i, $j ) {
@lines[$i].substr-rw( $start + $seg + $j, 1 ) = ' ';
}
 
_trim_middle_third( $seg, $start + $_, $index + 1 ) for 0, $seg * 2;
}
 
_trim_middle_third( $width, 0, 1 );
return @lines;
}
 
.say for cantor(5);
Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Phix[edit]

Based on Algol 68, but even simpler, shorter, and sweeter!

integer n = 5,
w = power(3,n-1),
len = w
string line = repeat('#',w)&"\n"
 
while 1 do
puts(1,line)
if len=1 then exit end if
len /= 3
integer pos = 1
while pos<(w-len) do
pos += len
line[pos..pos+len-1] = ' '
pos += len
end while
end while
Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

Racket[edit]

Translation of: Kotlin
#lang racket/base
;; {trans|Kotlin}}
 
(define current-width (make-parameter 81))
 
(define current-height (make-parameter 5))
 
(define (Cantor_set (w (current-width)) (h (current-height)))
(define lines (build-list h (λ (_) (make-bytes w (char->integer #\#)))))
(define (cantor start len index)
(let* ((seg (quotient len 3))
(seg-start (+ start seg))
(seg-end (+ seg-start seg)))
(unless (zero? seg)
(for* ((i (in-range index h))
(j (in-range seg-start seg-end)))
(bytes-set! (list-ref lines i) j (char->integer #\space)))
(cantor start seg (add1 index))
(cantor seg-end seg (add1 index)))))
(cantor 0 w 1)
lines)
 
(module+ main
(for-each displayln (Cantor_set)))
 
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

REXX[edit]

/*REXX program displays an ASCII diagram of a Canter Set as a set of (character) lines. */
w= linesize() /*obtain the width of the display term.*/
if w==0 then w=81 /*Can't obtain width? Use the default.*/
do lines=0; _=3**lines /*calculate powers of three (# lines).*/
if _>w then leave /*Too large? We passed the max value. */
#=_ /*this value of a width─of─line is OK. */
end /*lines*/ /* [↑] calculate a useable line width.*/
w= # /*use the (last) useable line width. */
$= copies('■', #) /*populate the display line with blocks*/
do j=0 until #==0 /*show Cantor set as a line of chars. */
if j>0 then do k=#+1 by #+# to w /*skip 1st line blanking.*/
$=overlay(left('', #), $, k) /*blank parts of a line. */
end /*j*/
say $ /*display a line of the Cantor Set. */
#= # % 3 /*the part (thirds) to be blanked out. */
end /*j*/ /*stick a fork in it, we're all done. */

This REXX program makes use of   linesize   REXX program (or BIF) which is used to determine the screen width (or linesize) of the terminal (console).

Some REXXes don't have this BIF, so the   linesize.rex   REXX program is included here   ──►   LINESIZE.REX.

output   when using the default size of the terminal width of 100:

(Shown at half size.)

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■■■■■■■■■■■■■                           ■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■         ■■■■■■■■■                           ■■■■■■■■■         ■■■■■■■■■
■■■   ■■■         ■■■   ■■■                           ■■■   ■■■         ■■■   ■■■
■ ■   ■ ■         ■ ■   ■ ■                           ■ ■   ■ ■         ■ ■   ■ ■
output   when using the default size of the terminal width of 250:

(Shown at half size.)

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■                                                                                 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■■■■■■■■■■■■■                           ■■■■■■■■■■■■■■■■■■■■■■■■■■■                                                                                 ■■■■■■■■■■■■■■■■■■■■■■■■■■■                           ■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■         ■■■■■■■■■                           ■■■■■■■■■         ■■■■■■■■■                                                                                 ■■■■■■■■■         ■■■■■■■■■                           ■■■■■■■■■         ■■■■■■■■■
■■■   ■■■         ■■■   ■■■                           ■■■   ■■■         ■■■   ■■■                                                                                 ■■■   ■■■         ■■■   ■■■                           ■■■   ■■■         ■■■   ■■■
■ ■   ■ ■         ■ ■   ■ ■                           ■ ■   ■ ■         ■ ■   ■ ■                                                                                 ■ ■   ■ ■         ■ ■   ■ ■                           ■ ■   ■ ■         ■ ■   ■ ■

Ring[edit]

 
# Project : Cantor set
 
load "guilib.ring"
paint = null
 
new qapp
{
win1 = new qwidget() {
setwindowtitle("")
setgeometry(100,100,800,600)
label1 = new qlabel(win1) {
setgeometry(10,10,800,600)
settext("")
}
new qpushbutton(win1) {
setgeometry(150,500,100,30)
settext("draw")
setclickevent("draw()")
}
show()
}
exec()
}
 
func draw
p1 = new qpicture()
color = new qcolor() {
setrgb(0,0,255,255)
}
pen = new qpen() {
setcolor(color)
setwidth(10)
}
paint = new qpainter() {
begin(p1)
setpen(pen)
 
cantor(10,20,600)
 
endpaint()
}
label1 { setpicture(p1) show() }
return
 
func cantor(x,y,lens)
if lens >= 10
paint.drawline(x,y,x+lens,y)
y = y + 20
cantor(x,y,floor(lens/3))
cantor(x+floor(lens*2/3),y,floor(lens/3))
ok
 

Output image:

Cantor set

Scala[edit]

Imperative Programming (Q&D)[edit]

object CantorSetQD extends App {
val (width, heigth) = (81, 5)
 
val lines = Seq.fill[Array[Char]](heigth)(Array.fill[Char](width)('*'))
 
def cantor(start: Int, len: Int, index: Int) {
val seg = len / 3
 
println(start, len, index)
 
if (seg != 0) {
for (i <- index until heigth;
j <- (start + seg) until (start + seg * 2)) lines(i)(j) = ' '
 
cantor(start, seg, index + 1)
cantor(start + seg * 2, seg, index + 1)
}
}
 
cantor(0, width, 1)
lines.foreach(l => println(l.mkString))
}
Output:
See it in running in your browser by (JavaScript)

or by Scastie (JVM).

Functional Programming (Recommended)[edit]

object CantorSetFP extends App {
val (width, heigth) = (81, 5)
 
def lines = (1 to heigth).map(_ => (0 until width).toSet)
 
def cantorSet(pre: Seq[Set[Int]], start: Int, len: Int, index: Int): Seq[Set[Int]] = {
val seg = len / 3
 
def cantorSet1(pre: Seq[Set[Int]], start: Int, index: Int): Seq[Set[Int]] = {
def elementsStuffing(pre: Set[Int], start: Int): Set[Int] =
pre -- ((start + seg) until (start + seg * 2))
 
for (n <- 0 until heigth)
yield if (index to heigth contains n) elementsStuffing(pre(n), start)
else pre(n)
}
 
if (seg == 0) pre
else {
def version0 = cantorSet1(pre, start, index)
def version1 = cantorSet(cantorSet1(pre, start, index), start, seg, index + 1)
 
cantorSet(version1, start + seg * 2, seg, index + 1)
}
}
 
def output: Seq[Set[Int]] = cantorSet(lines, 0, width, 1)
 
println(
output.map(l => (0 to width).map(pos => if (l contains pos) '*' else ' ').mkString)
.mkString("\n"))
}
Output:
See it in running in your browser by (JavaScript)

or by Scastie (JVM).

Sidef[edit]

Translation of: Perl 6
func cantor (height) {
var width = 3**(height - 1)
var lines = height.of { "\N{FULL BLOCK}" * width }
 
func trim_middle_third (len, start, index) {
var seg = (len // 3) || return()
 
for i, j in ((index ..^ height) ~X (0 ..^ seg)) {
lines[i].replace!(Regex("^.{#{start + seg + j}}\\K."), ' ')
}
 
[0, 2*seg].each { |k|
trim_middle_third(seg, start + k, index + 1)
}
}
 
trim_middle_third(width, 0, 1)
return lines
}
 
cantor(5).each { .say }
Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

zkl[edit]

const WIDTH=81, HEIGHT=5;
var lines=HEIGHT.pump(List,List.createLong(WIDTH,"\U2588;").copy); // full block
 
fcn cantor(start,len,index){
(seg:=len/3) or return();
foreach i,j in ([index..HEIGHT-1], [start + seg .. start + seg*2 - 1]){
lines[i][j]=" ";
}
cantor(start, seg, index + 1);
cantor(start + seg*2, seg, index + 1);
}(0,WIDTH,1);
 
lines.pump(Console.println,"concat");
Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █