Cantor set
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Draw a Cantor set.
See details at this Wikipedia webpage: Cantor set
11l
V WIDTH = 81
V HEIGHT = 5
F cantor(start, len, index)
V seg = len I/ 3
I seg == 0
R
L(it) 0 .< :HEIGHT - index
V i = index + it
L(jt) 0 .< seg
V j = start + seg + jt
V pos = i * :WIDTH + j
:lines[pos] = ‘ ’
cantor(start, seg, index + 1)
cantor(start + seg * 2, seg, index + 1)
V lines = [‘*’] * (WIDTH * HEIGHT)
cantor(0, WIDTH, 1)
L(i) 0 .< HEIGHT
V beg = WIDTH * i
print((lines[beg .< beg + WIDTH]).join(‘’))
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Action!
PROC FillRect(INT x,y,w,h)
INT i
FOR i=y TO y+h-1
DO
Plot(x,i)
DrawTo(x+w-1,i)
OD
RETURN
PROC DrawCantor(INT x0,y0,h,level)
INT x,y,i,j,w,w2,h2
w=1
FOR i=0 TO level-1
DO w==*3 OD
Color=1
y=y0
FOR i=0 TO level
DO
FillRect(x0,y,w,h)
y==+h*2
OD
Color=0
w2=1 h2=h*2
FOR i=0 TO level-1
DO
x=w2 y=(level-i)*(h*2)
WHILE x<w
DO
FillRect(x0+x,y0+y,w2,h2)
x==+w2*2
OD
w2==*3
h2==+h*2
OD
RETURN
PROC Main()
BYTE CH=$02FC,COLOR1=$02C5,COLOR2=$02C6
Graphics(8+16)
COLOR1=$0C
COLOR2=$02
DrawCantor(38,48,8,5)
DO UNTIL CH#$FF OD
CH=$FF
RETURN
- Output:
Screenshot from Atari 8-bit computer
Ada
with Ada.Text_IO;
procedure Cantor_Set is
subtype Level_Range is Integer range 1 .. 5;
Image : array (Level_Range) of String (1 .. 81) := (others => (others => ' '));
procedure Cantor (Level : Natural; Length : Natural; Start : Natural) is
begin
if Level in Level_Range then
Image (Level) (Start .. Start + Length - 1) := (others => '*');
Cantor (Level + 1, Length / 3, Start);
Cantor (Level + 1, Length / 3, Start + 2 * Length / 3);
end if;
end Cantor;
begin
Cantor (Level => Level_Range'First,
Length => 81,
Start => 1);
for L in Level_Range loop
Ada.Text_IO.Put_Line (Image (L));
end loop;
end Cantor_Set;
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
ALGOL 68
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
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:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
Amazing Hopper
VERSION 1:
#include <basico.h>
#define WIDTH 81
#define HEIGHT 5
#proto cantor(_X_,_Y_,_Z_)
algoritmo
decimales '0'
dimensionar(HEIGHT,WIDTH) matriz rellena("#",líneas)
_cantor(1, WIDTH, 2)
fijar separador 'NULO', imprimir( líneas, NL)
terminar
subrutinas
cantor(inicio, largo, índice)
seg=0
#( seg:=(int(largo/3))), no es cero?, entonces{
#basic{
líneas[índice:HEIGHT, (inicio+seg):((inicio+seg*2)-1)] = " ")
cantor( inicio, seg, índice+1 ) )
cantor( (inicio+(seg*2)), seg, índice+1 ) )
}
}
retornar
- Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
VERSION 2:
#include <basico.h>
#define HEIGHT 5
algoritmo
decimales '0'
cantor="", j=0
iterar
i=0
cadenas 's,v'
iterar grupo ( ++i, #(i< (3^j)),\
#( v = occurs("1", dectobase(i,3)) ? " " : "#"; )\
#( s = s $ replicate(v, 3^(HEIGHT-j-1) )) )
#(cantor = cantor $ s $ NL)
mientras ' #(j<=HEIGHT); ++j '
imprimir(cantor)
terminar
- Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
AppleScript
Using composable library functions whenever possible, for better productivity:
------------------------- CANTOR SET -----------------------
-- cantor :: [String] -> [String]
on cantor(xs)
script go
on |λ|(s)
set m to (length of s) div 3
set blocks to text 1 thru m of s
if "█" = text 1 of s then
{blocks, replicate(m, space), blocks}
else
{s}
end if
end |λ|
end script
concatMap(go, xs)
end cantor
---------------------------- TEST --------------------------
on run
showCantor(5)
end run
-- showCantor :: Int -> String
on showCantor(n)
unlines(map(my concat, ¬
take(n, iterate(cantor, ¬
{replicate(3 ^ (n - 1), "█")}))))
end showCantor
--------------------- GENERIC FUNCTIONS --------------------
-- concat :: [[a]] -> [a]
-- concat :: [String] -> String
on concat(xs)
set lng to length of xs
if 0 < lng and string is class of (item 1 of xs) then
set acc to ""
else
set acc to {}
end if
repeat with i from 1 to lng
set acc to acc & item i of xs
end repeat
acc
end concat
-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
set lng to length of xs
set acc to {}
tell mReturn(f)
repeat with i from 1 to lng
set acc to acc & |λ|(item i of xs, i, xs)
end repeat
end tell
return acc
end concatMap
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- iterate :: (a -> a) -> a -> Gen [a]
on iterate(f, x)
script
property v : missing value
property g : mReturn(f)'s |λ|
on |λ|()
if missing value is v then
set v to x
else
set v to g(v)
end if
return v
end |λ|
end script
end iterate
-- replicate :: Int -> String -> String
on replicate(n, s)
set out to ""
if n < 1 then return out
set dbl to s
repeat while (n > 1)
if (n mod 2) > 0 then set out to out & dbl
set n to (n div 2)
set dbl to (dbl & dbl)
end repeat
return out & dbl
end replicate
-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
set c to class of xs
if list is c then
if 0 < n then
items 1 thru min(n, length of xs) of xs
else
{}
end if
else if string is c then
if 0 < n then
text 1 thru min(n, length of xs) of xs
else
""
end if
else if script is c then
set ys to {}
repeat with i from 1 to n
set v to xs's |λ|()
if missing value is v then
return ys
else
set end of ys to v
end if
end repeat
return ys
else
missing value
end if
end take
-- unlines :: [String] -> String
on unlines(xs)
set {dlm, my text item delimiters} to ¬
{my text item delimiters, linefeed}
set str to xs as text
set my text item delimiters to dlm
str
end unlines
- Output:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
Arturo
width: 81
height: 5
lines: array.of: height repeat `*` width
cantor: function [start length idx].export:[lines][
seg: length / 3
if seg = 0 -> return null
loop idx..dec height 'i [
loop (start + seg).. dec start + 2 * seg 'j
-> set lines\[i] j ` `
]
cantor start seg idx+1
cantor start + 2 * seg seg idx+1
]
cantor 0 width 1
loop lines 'line
-> print join line
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
AWK
# 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:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
BASIC
10 DEFINT A-Z
20 N = 4
30 W = 3^(N-1)
40 S = W
50 L$ = STRING$(W, "#")
60 PRINT L$
70 IF S = 1 THEN END
80 S = S\3
90 P = 1
100 IF P >= W-S GOTO 60
110 P = P+S
120 MID$(L$,P,S) = SPACE$(S)
130 P = P+S
140 GOTO 100
- Output:
########################### ######### ######### ### ### ### ### # # # # # # # #
BASIC256
global ancho, alto, intervalo
ancho = 81 : alto = 5
dim intervalo(alto, ancho)
subroutine Cantor()
for i = 0 to alto - 1
for j = 0 to ancho - 1
intervalo[i, j] = "■"
next j
next i
end subroutine
subroutine ConjCantor(inicio, longitud, indice)
segmento = longitud / 3
if segmento = 0 then return
for i = indice to alto - 1
for j = inicio + segmento to inicio + segmento * 2 - 1
intervalo[i, j] = " "
next j
next i
call ConjCantor(inicio, segmento, indice + 1)
call ConjCantor(inicio + segmento * 2, segmento, indice + 1)
end subroutine
call Cantor()
call ConjCantor(0, ancho, 1)
for i = 0 to alto - 1
for j = 0 to ancho - 1
print intervalo[i, j];
next j
print
next i
end
Chipmunk Basic
100 cls
110 ancho = 81
120 alto = 5
130 dim intervalo$(alto,ancho)
140 '
150 sub cantor()
160 for i = 0 to alto-1
170 for j = 0 to ancho-1
180 intervalo$(i,j) = chr$(254)
190 next j
200 next i
210 end sub
220 '
230 sub conjcantor(inicio,longitud,indice)
240 segmento = longitud/3
250 if segmento = 0 then exit sub
260 for i = indice to alto-1
270 for j = inicio+segmento to inicio+segmento*2-1
280 intervalo$(i,j) = chr$(32)
290 next j
300 next i
310 conjcantor(inicio,segmento,indice+1)
320 conjcantor(inicio+segmento*2,segmento,indice+1)
330 end sub
340 '
350 cantor()
360 conjcantor(0,ancho,1)
370 for i = 0 to alto-1
380 for j = 0 to ancho-1
390 print intervalo$(i,j);
400 next j
410 print
420 next i
430 end
GW-BASIC
The BASIC solution works without any changes.
MSX Basic
The BASIC solution works without any changes.
QBasic
SUB Cantor
FOR i = 0 TO alto - 1
FOR j = 0 TO ancho - 1
intervalo$(i, j) = CHR$(254) '"#"
NEXT j
NEXT i
END SUB
SUB ConjCantor (inicio, longitud, indice)
segmento = INT(longitud / 3)
IF segmento = 0 THEN EXIT SUB
FOR i = indice TO alto - 1
FOR j = inicio + segmento TO inicio + segmento * 2 - 1
intervalo$(i, j) = CHR$(32) '" "
NEXT j
NEXT i
CALL ConjCantor(inicio, segmento, indice + 1)
CALL ConjCantor(inicio + segmento * 2, segmento, indice + 1)
END SUB
CONST ancho = 81
CONST alto = 5
DIM SHARED intervalo$(alto, ancho)
CLS
CALL Cantor
CALL ConjCantor(0, ancho, 1)
FOR i = 0 TO alto - 1
FOR j = 0 TO ancho - 1
PRINT intervalo$(i, j);
NEXT j
PRINT
NEXT i
END
True BASIC
LET ancho = 81
LET alto = 5
DIM intervalo$(0,0)
MAT REDIM intervalo$(0 TO alto, 0 TO ancho)
SUB cantor
FOR i = 0 TO alto-1
FOR j = 0 TO ancho-1
LET intervalo$(i, j) = "#" !CHR$(254)
NEXT j
NEXT i
END SUB
SUB conjcantor (inicio,longitud,indice)
LET segmento = INT(longitud/3)
IF segmento = 0 THEN EXIT SUB
FOR i = indice TO alto-1
FOR j = inicio+segmento TO inicio+segmento*2-1
LET intervalo$(i, j) = CHR$(32) !" "
NEXT j
NEXT i
CALL conjcantor (inicio, segmento, indice+1)
CALL conjcantor (inicio+segmento*2, segmento, indice+1)
END SUB
CALL cantor
CALL conjcantor (0, ancho, 1)
FOR i = 0 TO alto-1
FOR j = 0 TO ancho-1
PRINT intervalo$(i, j);
NEXT j
PRINT
NEXT i
END
Yabasic
ancho = 81
alto = 5
dim intervalo$(alto, ancho)
Cantor()
ConjCantor(0, ancho, 1)
for i = 0 to alto - 1
for j = 0 to ancho - 1
print intervalo$(i, j);
next j
print
next i
end
sub Cantor()
for i = 0 to alto - 1
for j = 0 to ancho - 1
intervalo$(i, j) = chr$(254) //"#"
next j
next i
end sub
sub ConjCantor(inicio, longitud, indice)
segmento = longitud / 3
if segmento = 0 return
for i = indice to alto - 1
for j = inicio + segmento to inicio + segmento * 2 - 1
intervalo$(i, j) = " "
next j
next i
ConjCantor(inicio, segmento, indice + 1)
ConjCantor(inicio + segmento * 2, segmento, indice + 1)
end sub
BQN
Cantor ← {" •" ⊏˜ >⥊¨(¯1⊸⊏⊢¨¨⊢)1‿0‿1∧⌜⍟(↕𝕩)1}
- Output:
Cantor 5 ┌─ ╵"••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• ••••••••••••••••••••••••••• ••••••••••••••••••••••••••• ••••••••• ••••••••• ••••••••• ••••••••• ••• ••• ••• ••• ••• ••• ••• ••• • • • • • • • • • • • • • • • •" ┘
C
#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#
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:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
C++
#include <iostream>
const int WIDTH = 81;
const int HEIGHT = 5;
char lines[WIDTH*HEIGHT];
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 + 2 * seg, seg, index + 1);
}
int main() {
// init
for (int i = 0; i < WIDTH*HEIGHT; i++) {
lines[i] = '*';
}
// calculate
cantor(0, WIDTH, 1);
// print
for (int i = 0; i < HEIGHT*WIDTH; i += WIDTH) {
printf("%.*s\n", WIDTH, lines + i);
}
return 0;
}
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
CLU
cantor = cluster is make
rep = null
ac = array[char]
aac = array[array[char]]
make = proc (width, height: int, ch: char) returns (string)
lines: aac := aac$fill_copy(0, height, ac$fill(0, width, ch))
cantor_step(lines, 0, width, 1)
s: stream := stream$create_output()
for line: ac in aac$elements(lines) do
stream$putl(s, string$ac2s(line))
end
return(stream$get_contents(s))
end make
cantor_step = proc (lines: aac, start, len, index: int)
seg: int := len / 3
if seg = 0 then return end
for i: int in int$from_to(index, aac$high(lines)) do
for j: int in int$from_to(start+seg, start+seg*2-1) do
lines[i][j] := ' '
end
end
cantor_step(lines, start, seg, index+1)
cantor_step(lines, start+seg*2, seg, index+1)
end cantor_step
end cantor
start_up = proc ()
po: stream := stream$primary_output()
cs: string := cantor$make(81, 5, '*')
stream$puts(po, cs)
end start_up
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
COBOL
IDENTIFICATION DIVISION.
PROGRAM-ID. CANTOR.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 SETTINGS.
03 NUM-LINES PIC 9 VALUE 5.
03 FILL-CHAR PIC X VALUE '#'.
01 VARIABLES.
03 CUR-LINE.
05 CHAR PIC X OCCURS 81 TIMES.
03 WIDTH PIC 99.
03 CUR-SIZE PIC 99.
03 POS PIC 99.
03 MAXPOS PIC 99.
03 NEXTPOS PIC 99.
03 I PIC 99.
PROCEDURE DIVISION.
BEGIN.
COMPUTE WIDTH = 3 ** (NUM-LINES - 1).
PERFORM INIT.
MOVE WIDTH TO CUR-SIZE.
DISPLAY CUR-LINE.
PERFORM DO-LINE UNTIL CUR-SIZE IS EQUAL TO 1.
STOP RUN.
INIT.
PERFORM INIT-CHAR VARYING I FROM 1 BY 1
UNTIL I IS GREATER THAN WIDTH.
INIT-CHAR.
MOVE FILL-CHAR TO CHAR(I).
DO-LINE.
DIVIDE 3 INTO CUR-SIZE.
MOVE 1 TO POS.
SUBTRACT CUR-SIZE FROM WIDTH GIVING MAXPOS.
PERFORM BLANK-REGIONS UNTIL POS IS GREATER THAN MAXPOS.
DISPLAY CUR-LINE.
BLANK-REGIONS.
ADD CUR-SIZE TO POS.
PERFORM BLANK-CHAR CUR-SIZE TIMES.
BLANK-CHAR.
MOVE SPACE TO CHAR(POS).
ADD 1 TO POS.
- Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
D
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:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Delphi
program Cantor_set;
{$APPTYPE CONSOLE}
const
WIDTH: Integer = 81;
HEIGHT: Integer = 5;
var
Lines: TArray<TArray<Char>>;
procedure Init;
var
i, j: Integer;
begin
SetLength(lines, HEIGHT, WIDTH);
for i := 0 to HEIGHT - 1 do
for j := 0 to WIDTH - 1 do
lines[i, j] := '*';
end;
procedure Cantor(start, len, index: Integer);
var
seg, i, j: Integer;
begin
seg := len div 3;
if seg = 0 then
Exit;
for i := index to HEIGHT - 1 do
for j := start + seg to start + seg * 2 - 1 do
lines[i, j] := ' ';
Cantor(start, seg, index + 1);
Cantor(start + seg * 2, seg, index + 1);
end;
var
i, j: Integer;
begin
Init;
Cantor(0, WIDTH, 1);
for i := 0 to HEIGHT - 1 do
begin
for j := 0 to WIDTH - 1 do
Write(lines[i, j]);
Writeln;
end;
Readln;
end.
- Output:
Same result of Java
EasyLang
color 555
proc cantor x y sz . .
if sz > 0.1
sz3 = sz / 3
move x y - sz3
rect sz sz3
cantor x y - sz3 sz3
cantor x + 2 * sz3 y - sz3 sz3
.
.
cantor 0 80 100
Elixir
defmodule Cantor do
@pos "█"
@neg " "
def run(lines) do
Enum.map(0..lines, fn line ->
segment_size = 3 ** (lines - line - 1)
chars = (3 ** line)
Enum.map(0..chars, fn char ->
char
|> Integer.digits(3)
|> Enum.any?(fn x -> x === 1 end)
|> case do
true -> @neg
false -> @pos
end
end)
|> Enum.reduce([], fn el, acc -> duplicate_char(acc, el, segment_size) end)
|> Enum.join()
|> String.trim_trailing()
end)
|> Enum.filter(fn line -> line !== "" end)
end
def duplicate_char(acc, el, segment_size) when segment_size >= 1, do: acc ++ [String.duplicate(el, segment_size)]
def duplicate_char(acc, _el, segment_size) when segment_size < 1, do: acc
end
Cantor.run(5) |> IO.inspect()
- Output:
["█████████████████████████████████████████████████████████████████████████████████", "███████████████████████████ ███████████████████████████", "█████████ █████████ █████████ █████████", "███ ███ ███ ███ ███ ███ ███ ███", "█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █"]
EMal
int WIDTH = 81
int HEIGHT = 5
List lines = text[].with(HEIGHT) # a list with HEIGHT empty texts
for each int i in range(0, HEIGHT) do lines[i] = text("█", WIDTH) end
fun cantor = void by int start, int len, int index
int seg = len / 3
if seg == 0 do return end
for int i = index; i < HEIGHT; i++
for int j = start + seg; j < start + seg * 2; j++
lines[i][j] = " "
end
end
cantor(start, seg, index + 1)
cantor(start + seg * 2, seg, index + 1)
end
cantor(0, WIDTH, 1)
for each text line in lines do writeLine(line) end
- Output:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
Excel
LAMBDA
Binding names to the following lambda expressions in the Name Manager of the Excel WorkBook:
(See LAMBDA: The ultimate Excel worksheet function)
CANTOR
=LAMBDA(n,
APPLYN(n)(
LAMBDA(grid,
APPENDROWS(grid)(
CANTOROW(
LASTROW(grid)
)
)
)
)({0,1})
)
CANTOROW
=LAMBDA(xys,
LET(
nCols, COLUMNS(xys),
IF(2 > nCols,
xys,
IF(3 < nCols,
APPENDCOLS(
CANTORSLICES(TAKECOLS(2)(xys))
)(
CANTOROW(DROPCOLS(2)(xys))
),
CANTORSLICES(TAKECOLS(2)(xys))
)
)
)
)
CANTORSLICES
=LAMBDA(ab,
LET(
a, INDEX(ab, 1),
b, INDEX(ab, 2),
third, (b - a) / 3,
CHOOSE({1,2,3,4}, a, a + third, b - third, b)
)
)
SHOWCANTOR
=LAMBDA(grid,
LET(
leaves, LASTROW(grid),
leafWidth, INDEX(leaves, 1, 2) - INDEX(leaves, 1, 1),
leafCount, 1 / leafWidth,
SHOWCANTROWS(leafCount)(grid)
)
)
SHOWCANTROWS
=LAMBDA(leafCount,
LAMBDA(grid,
LET(
xs, FILTERP(
LAMBDA(x, NOT(ISNA(x)))
)(
HEADCOL(grid)
),
runLengths, LAMBDA(x,
CEILING.MATH(leafCount * x))(
SUBTRACT(TAILROW(xs))(INITROW(xs)
)
),
iCols, SEQUENCE(1, COLUMNS(runLengths)),
CONCAT(
REPT(
IF(ISEVEN(iCols), " ", "█"),
runLengths
)
) & IF(1 < ROWS(grid),
CHAR(10) & SHOWCANTROWS(leafCount)(
TAILCOL(grid)
),
""
)
)
)
)
and also assuming the following generic bindings in the Name Manager for the WorkBook:
APPENDCOLS
=LAMBDA(xs,
LAMBDA(ys,
LET(
nx, COLUMNS(xs),
ny, COLUMNS(ys),
colIndexes, SEQUENCE(1, nx + ny),
rowIndexes, SEQUENCE(MAX(ROWS(xs), ROWS(ys))),
IFERROR(
IF(nx < colIndexes,
INDEX(ys, rowIndexes, colIndexes - nx),
INDEX(xs, rowIndexes, colIndexes)
),
NA()
)
)
)
)
APPENDROWS
=LAMBDA(xs,
LAMBDA(ys,
LET(
nx, ROWS(xs),
rowIndexes, SEQUENCE(nx + ROWS(ys)),
colIndexes, SEQUENCE(
1,
MAX(COLUMNS(xs), COLUMNS(ys))
),
IFERROR(
IF(rowIndexes <= nx,
INDEX(xs, rowIndexes, colIndexes),
INDEX(ys, rowIndexes - nx, colIndexes)
),
NA()
)
)
)
)
APPLYN
=LAMBDA(n,
LAMBDA(f,
LAMBDA(x,
IF(0 < n,
APPLYN(n - 1)(f)(
f(x)
),
x
)
)
)
)
DROPCOLS
=LAMBDA(n,
LAMBDA(xs,
INDEX(
xs,
SEQUENCE(ROWS(xs), 1),
SEQUENCE(1, (COLUMNS(xs) - n), 1 + n, 1)
)
)
)
FILTERP
=LAMBDA(p,
LAMBDA(xs,
FILTER(xs, p(xs))
)
)
HEADCOL
=LAMBDA(xs,
LET(REM, "The first item of each column in xs",
INDEX(xs, 1, SEQUENCE(1, COLUMNS(xs)))
)
)
INITROW
=LAMBDA(xs,
INDEX(
xs,
SEQUENCE(
1,
COLUMNS(xs) - 1,
1, 1
)
)
)
LASTROW
=LAMBDA(xs,
INDEX(
xs,
ROWS(xs),
SEQUENCE(1, COLUMNS(xs), 1, 1)
)
)
SUBTRACT
=LAMBDA(a,
LAMBDA(b, a - b)
)
TAILCOL
=LAMBDA(cols,
LET(REM, "The tail of each column in the grid.",
INDEX(
cols,
SEQUENCE(ROWS(cols) - 1, 1, 2, 1),
SEQUENCE(1, COLUMNS(cols))
)
)
)
TAILROW
=LAMBDA(xs,
LET(REM,"The tail of each row in the grid",
n, COLUMNS(xs) - 1,
IF(0 < n,
INDEX(
xs,
SEQUENCE(ROWS(xs), 1, 1, 1),
SEQUENCE(1, n, 2, 1)
),
NA()
)
)
)
TAKECOLS
=LAMBDA(n,
LAMBDA(xs,
INDEX(
xs,
SEQUENCE(ROWS(xs)),
SEQUENCE(1, n)
)
)
)
- Output:
As a string, with the format of the cell set to a mono-spaced font, and with Format > Cells > Alignment > Wrap text = True, to allow for the display of Excel's (platform - independent) CHAR(10) line breaks:
fx | =SHOWCANTOR(CANTOR(1)) | ||
---|---|---|---|
A | B | ||
1 | SHOWCANTOR(CANTOR(0)) | █
| |
2 | SHOWCANTOR(CANTOR(1)) | ███
█ █
| |
3 | SHOWCANTOR(CANTOR(2)) | █████████
███ ███
█ █ █ █
| |
4 | SHOWCANTOR(CANTOR(3)) | ███████████████████████████
█████████ █████████
███ ███ ███ ███
█ █ █ █ █ █ █ █
|
The diagrams above are drawn from the array of underlying fractions defined by CANTOR(n).
(The expression in B2 below defines the values which populate the grid B2:Q5)
The enclosing application of the built-in IFNA function maps undefined numeric cells in that grid (with the Excel value #N/A) to empty strings, for a more readable display.
(The number format for the numeric cells is set to Fraction > Up to three digits.)
fx | =IFNA(CANTOR(3), "") | |||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | ||
1 | ||||||||||||||||||
2 | Unit range | 0 | 1 | |||||||||||||||
3 | Cantor 1 | 0 | 1/3 | 2/3 | 1 | |||||||||||||
4 | Cantor 2 | 0 | 1/9 | 2/9 | 1/3 | 2/3 | 7/9 | 8/9 | 1 | |||||||||
5 | Cantor 3 | 0 | 1/27 | 2/27 | 1/9 | 2/9 | 7/27 | 8/27 | 1/3 | 2/3 | 19/27 | 20/27 | 7/9 | 8/9 | 25/27 | 26/27 | 1 |
Factor
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:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
Forth
Where is says [email protected]
it should say c@
, but I'm not keen on writing it as c@
in the actual code.
warnings off
4 \ iterations
: ** 1 swap 0 ?DO over * LOOP nip ;
3 swap ** constant width \ Make smallest step 1
create string here width char # fill width allot
: print string width type cr ;
\ Overwrite string with new holes of size 'length'.
\ Pointer into string at TOS.
create length width ,
: reduce length dup @ 3 / swap ! ;
: done? dup string - width >= ;
: hole? dup c@ bl = ;
: skip length @ + ;
: whipe dup length @ bl fill skip ;
: step hole? IF skip skip skip ELSE skip whipe skip THEN ;
: split reduce string BEGIN step done? UNTIL drop ;
\ Main
: done? length @ 1 <= ;
: step split print ;
: go print BEGIN step done? UNTIL ;
go bye
Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
FreeBASIC
Const ancho = 81
Const alto = 5
Dim Shared intervalo(alto, ancho) As String
Dim As Integer i, j
Sub Cantor()
Dim As Integer i, j
For i = 0 To alto - 1
For j = 0 To ancho - 1
intervalo(i, j) = Chr(254)
Next j
Next i
End Sub
Sub ConjCantor(inicio As Integer, longitud As Integer, indice As Integer)
Dim As Integer i, j
Dim segmento As Integer = longitud / 3
If segmento = 0 Then Return
For i = indice To alto - 1
For j = inicio + segmento To inicio + segmento * 2 - 1
intervalo(i, j) = Chr(32)
Next j
Next i
ConjCantor(inicio, segmento, indice + 1)
ConjCantor(inicio + segmento * 2, segmento, indice + 1)
End Sub
Cantor()
ConjCantor(0, ancho, 1)
For i = 0 To alto - 1
For j = 0 To ancho - 1
Print intervalo(i, j);
Next j
Print
Next i
End
- Output:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
FutureBasic
_window = 1
_width = 81
_height = 5
window _window, @"FutureBasic Cantor Set", ( 0, 0, 695, 100 )
WindowSetBackgroundColor( _window, fn ColorWhite )
text @"Menlo", 14.0, fn ColorRed
begin globals
CFStringRef gInterval( _height, _width )
end globals
local fn Init
NSInteger i, j
for i = 0 to _height - 1
for j = 0 to _width - 1
gInterval( i, j ) = @"◼︎"
next
next
end fn
local fn CantorSet( start as NSInteger, length as NSInteger, index as NSInteger )
NSInteger i, j, segment = length / 3
if segment == 0 then exit fn
for i = index to _height - 1
for j = start + segment to start + segment * 2 - 1
gInterval( i, j ) = @" "
next
next
fn CantorSet( start, segment, index + 1 )
fn CantorSet( start + segment * 2, segment, index + 1 )
end fn
NSInteger i, j
fn Init
fn CantorSet ( 0, _width, 1 )
for i = 0 to _height - 1
for j = 0 to _width - 1
print gInterval( i, j );
next
print
next
HandleEvents
- Output:
Fōrmulæ
Fōrmulæ programs are not textual, visualization/edition of programs is done showing/manipulating structures but not text. Moreover, there can be multiple visual representations of the same program. Even though it is possible to have textual representation —i.e. XML, JSON— they are intended for storage and transfer purposes more than visualization and edition.
Programs in Fōrmulæ are created/edited online in its website.
In this page you can see and run the program(s) related to this task and their results. You can also change either the programs or the parameters they are called with, for experimentation, but remember that these programs were created with the main purpose of showing a clear solution of the task, and they generally lack any kind of validation.
Solution
Strictly speaking, a visualization of the perfect Cantor set is impossible, it consists of a infinite set of isolated points in the interval [0, 1]. The most we can do is drawing the early steps of its construction.
Several ways of visualization of the Cantor set are shown.
Preliminaries
Let us start with a definition of the points that define the central points of the intervals of the Cantos set on any step:
The followign function defines the segments on each step:
Plotting the central points
Common method
Cantor dust
Knaster–Kuratowski fan or "Cantor teepee"
The following forms of visualization are taken from point-set topology. They are not intended as forms of visualization of the Cantor set, instead, these structures make use of the Cantor set.
Brouwer–Janiszewski–Knaster continuum, or "the bucket handle"
"Double Knaster"
Go
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:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Groovy
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 = (int) (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++) {
System.out.print(lines[i][j])
}
System.out.println()
}
}
}
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Haskell
Interval bars
(Functional version)
-------------------------- CANTOR ------------------------
cantor :: [(Bool, Int)] -> [(Bool, Int)]
cantor = concatMap go
where
go (bln, n)
| bln && 1 < n =
let m = quot n 3
in [(True, m), (False, m), (True, m)]
| otherwise = [(bln, n)]
--------------------------- TEST -------------------------
main :: IO ()
main = putStrLn $ cantorLines 5
------------------------- DISPLAY ------------------------
cantorLines :: Int -> String
cantorLines n =
unlines $
showCantor
<$> take n (iterate cantor [(True, 3 ^ pred n)])
showCantor :: [(Bool, Int)] -> String
showCantor = concatMap $ uncurry (flip replicate . c)
where
c True = '*'
c False = ' '
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Or, using strings for the model as well as the display:
-------------------------- CANTOR ------------------------
cantor :: [String] -> [String]
cantor = (go =<<)
where
go x
| '█' == head x = [block, replicate m ' ', block]
| otherwise = [x]
where
m = quot (length x) 3
block = take m x
--------------------------- TEST -------------------------
main :: IO ()
main = putStrLn $ cantorLines 5
------------------------- DISPLAY ------------------------
cantorLines :: Int -> String
cantorLines =
unlines . (concat <$>)
. ( take
<*> ( iterate cantor
. return
. flip replicate '█'
. (3 ^)
. pred
)
)
- Output:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
Dual representation
Intervals as fraction pairs, and intervals as graphic bars:
import Control.Monad (join)
import Data.Bifunctor (bimap)
import Data.List (intercalate, mapAccumL, maximumBy)
import Data.Ratio (Ratio, denominator, numerator, (%))
-------------------------- CANTOR ------------------------
cantor :: (Rational, Rational) -> [[(Rational, Rational)]]
cantor = iterate (go =<<) . pure
where
go (x, y) = [(x, x + r), (y - r, y)]
where
r = (y - x) / 3
--------------------------- TEST -------------------------
main :: IO ()
main =
( ( (>>)
. putStrLn
. unlines
. fmap intervalRatios
)
<*> (putStrLn . intervalBars)
)
$ take 4 $ cantor (0, 1)
------------------------- DISPLAY ------------------------
intervalBars :: [[(Rational, Rational)]] -> String
intervalBars xs = unlines $ go (d % 1) <$> xs
where
d = maximum $ denominator . fst <$> last xs
go w xs =
concat . snd $
mapAccumL
( \a (rx, ry) ->
let (wy, wx) = (w * ry, w * rx)
in ( wy,
replicate (floor (wx - a)) ' '
<> replicate (floor (wy - wx)) '█'
)
)
0
xs
intervalRatios :: [(Rational, Rational)] -> String
intervalRatios =
('(' :) . (<> ")")
. intercalate ") ("
. fmap
(uncurry ((<>) . (<> ", ")) . join bimap showRatio)
showRatio :: Rational -> String
showRatio = ((<>) . show . numerator) <*> (go . denominator)
where
go x
| 1 /= x = '/' : show x
| otherwise = []
- Output:
(0, 1) (0, 1/3) (2/3, 1) (0, 1/9) (2/9, 1/3) (2/3, 7/9) (8/9, 1) (0, 1/27) (2/27, 1/9) (2/9, 7/27) (8/27, 1/3) (2/3, 19/27) (20/27, 7/9) (8/9, 25/27) (26/27, 1) ███████████████████████████ █████████ █████████ ███ ███ ███ ███ █ █ █ █ █ █ █ █
IS-BASIC
100 PROGRAM "Cantor.bas"
110 GRAPHICS HIRES 2
120 SET PALETTE BLACK,WHITE
130 CALL CANTOR(28,500,1216,32)
140 DEF CANTOR(X,Y,L,HEIGHT)
150 IF L>3 THEN
160 PLOT X,Y;X+L,Y,X,Y+4;X+L,Y+4
170 CALL CANTOR(X,Y-HEIGHT,L/3,HEIGHT)
180 CALL CANTOR(X+2*L/3,Y-HEIGHT,L/3,HEIGHT)
190 END IF
200 END DEF
J
The argument to the cantor_dust monad is an integer that describes the depth of the dust. Shown here are results for cantor_dust 2 and for cantor_dust 3 . It works by checking for 1 digits in the base 3 representation of the coordinates. These background coordinates are plotted with # character using ASCII art. 1j1 #"1 expands the lines to improve aspect ratio on character cell (console) display. }:"1 curtails the extra space character line by line. < draws a pretty box.
odometer =: [: (4 $. $.) $&1
cantor_dust =: monad define
shape =. ,~ 3 ^ y
a =. shape $ ' '
i =. odometer shape
< (}:"1) 1j1 #"1 '#' (([: <"1 [: ;/"1 (#~ 1 e."1 [: (,/"2) 3 3&#:)) i)}a
)
cantor_dust 2 ┌─────────────────┐ │ # # # # # │ │# # # # # # # # #│ │ # # # # # │ │# # # # # # # # #│ │# # # # # # # # #│ │# # # # # # # # #│ │ # # # # # │ │# # # # # # # # #│ │ # # # # # │ └─────────────────┘ cantor_dust 3 ┌─────────────────────────────────────────────────────┐ │ # # # # # # # # # # # # # # # │ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │ # # # # # # # # # # # # # # # │ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │ # # # # # # # # # # # # # # # │ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │ # # # # # # # # # # # # # # # │ │ # # # # # # # # # # # # # # # │ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │ # # # # # # # # # # # # # # # │ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │ # # # # # # # # # # # # # # # │ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │ # # # # # # # # # # # # # # # │ │ # # # # # # # # # # # # # # # │ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │ # # # # # # # # # # # # # # # │ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │ # # # # # # # # # # # # # # # │ │# # # # # # # # # # # # # # # # # # # # # # # # # # #│ │ # # # # # # # # # # # # # # # │ └─────────────────────────────────────────────────────┘
With an `x' argument cantor_dust generalizes to higher dimensions. Try 3 cantor_dust 2
cantor_dust =: 2&$: :(dyad define)
shape =. x # 3 ^ y
a =. shape $ ' '
i =. odometer shape
< (}:"1) 1j1 #"1 '#' (([: <"1 [: ;/"1 (#~ 1 e."1 [: (,/"2) 3 3&#:)) i)} a
)
Java
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:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
JavaScript
Cantor: (Bool, Int) pairs
(Functional version)
(() => {
"use strict";
// -------------- CANTOR BOOL-INT PAIRS --------------
// cantor :: [(Bool, Int)] -> [(Bool, Int)]
const cantor = xs => {
const go = ([bln, n]) =>
bln && 1 < n ? (() => {
const x = Math.floor(n / 3);
return [
[true, x],
[false, x],
[true, x]
];
})() : [
[bln, n]
];
return xs.flatMap(go);
};
// ---------------------- TEST -----------------------
// main :: IO ()
const main = () =>
cantorLines(5);
// --------------------- DISPLAY ---------------------
// cantorLines :: Int -> String
const cantorLines = n =>
take(n)(
iterate(cantor)([
[true, 3 ** (n - 1)]
])
)
.map(showCantor)
.join("\n");
// showCantor :: [(Bool, Int)] -> String
const showCantor = xs =>
xs.map(
([bln, n]) => (
bln ? (
"*"
) : " "
).repeat(n)
)
.join("");
// ---------------- GENERIC FUNCTIONS ----------------
// iterate :: (a -> a) -> a -> Gen [a]
const iterate = f =>
// An infinite list of repeated
// applications of f to x.
function* (x) {
let v = x;
while (true) {
yield v;
v = f(v);
}
};
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n =>
// The first n elements of a list,
// string of characters, or stream.
xs => "GeneratorFunction" !== xs
.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat(...Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// MAIN ---
return main();
})();
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Cantor: Strings
Using strings for the model as well as the display:
(() => {
"use strict";
// ----------------- CANTOR STRINGS ------------------
// cantor :: [String] -> [String]
const cantor = xs => {
const go = s => {
const
m = Math.floor(s.length / 3),
blocks = take(m)(s);
return "█" === s[0] ? (
[blocks, " ".repeat(m), blocks]
) : [s];
};
return xs.flatMap(go);
};
// ---------------------- TEST -----------------------
const main = () =>
showCantor(5);
// --------------------- DISPLAY ---------------------
// showCantor :: Int -> String
const showCantor = n =>
take(n)(
iterate(cantor)([
"█".repeat(3 ** (n - 1))
])
)
.map(x => x.join(""))
.join("\n");
// ---------------- GENERIC FUNCTIONS ----------------
// iterate :: (a -> a) -> a -> Gen [a]
const iterate = f =>
// An infinite list of repeated
// applications of f to x.
function* (x) {
let v = x;
while (true) {
yield v;
v = f(v);
}
};
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n =>
// The first n elements of a list,
// string of characters, or stream.
xs => "GeneratorFunction" !== xs
.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat(...Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// MAIN ---
return main();
})();
- Output:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
Cantor: Rational pairs
Cantor ternary intervals rendered both as lists of rational pairs, and as graphic bars.
In the case of languages like Javascript which lack a built-in Ratio type, or standard Fraction/Ratio library, rendering stages of the set elaboration as lists of fraction pairs may take more work than rendering them as graphic lines.
(() => {
"use strict";
// -------------- CANTOR RATIONAL PAIRS --------------
// cantor :: [(Rational, Rational)] ->
// [(Rational, Rational)]
const cantor = xs => {
const go = ab => {
const [r1, r2] = Array.from(ab).map(rational);
const third = ratioDiv(ratioMinus(r2)(r1))(3);
return [
Tuple(r1)(ratioPlus(r1)(third)),
Tuple(ratioMinus(r2)(third))(r2)
];
};
return xs.flatMap(go);
};
// ---------------------- TEST -----------------------
// main :: IO ()
const main = () => {
const
xs = take(4)(
iterate(cantor)([Tuple(0)(1)])
);
return [
`${unlines(xs.map(intervalRatios))}\n`,
intervalBars(xs)
]
.join("\n\n");
};
// --------------------- DISPLAY ---------------------
// intervalRatios :: [(Rational, Rational)] -> String
const intervalRatios = xs => {
const go = ab =>
Array.from(ab).map(
compose(showRatio, rational)
)
.join(", ");
return `(${xs.map(go).join(") (")})`;
};
// intervalBars :: [[(Rational, Rational)]] -> String
const intervalBars = rs => {
const go = w => xs =>
snd(mapAccumL(
a => ab => {
const [wx, wy] = Array.from(ab).map(
r => ratioMult(w)(
rational(r)
)
);
return Tuple(wy)(
replicateString(
floor(ratioMinus(wx)(a))
)(" ") + replicateString(
floor(ratioMinus(wy)(wx))
)("█")
);
}
)(0)(xs)).join("");
const d = maximum(
last(rs).map(x => fst(x).d)
);
return unlines(rs.map(
go(Ratio(d)(1))
));
};
// ---------------- GENERIC FUNCTIONS ----------------
// Ratio :: Integral a => a -> a -> Ratio a
const Ratio = a => b => {
const go = (x, y) =>
0 !== y ? (() => {
const d = gcd(x)(y);
return {
type: "Ratio",
// numerator
"n": Math.trunc(x / d),
// denominator
"d": Math.trunc(y / d)
};
})() : undefined;
return go(a * signum(b), abs(b));
};
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a =>
b => ({
type: "Tuple",
"0": a,
"1": b,
length: 2
});
// abs :: Num -> Num
const abs =
// Absolute value of a given number
// without the sign.
x => 0 > x ? (
-x
) : x;
// approxRatio :: Float -> Float -> Ratio
const approxRatio = eps =>
n => {
const
gcde = (e, x, y) => {
const _gcd = (a, b) =>
b < e ? (
a
) : _gcd(b, a % b);
return _gcd(Math.abs(x), Math.abs(y));
},
c = gcde(Boolean(eps) ? (
eps
) : (1 / 10000), 1, n);
return Ratio(
Math.floor(n / c)
)(
Math.floor(1 / c)
);
};
// floor :: Num -> Int
const floor = x => {
const
nr = (
"Ratio" !== x.type ? (
properFraction
) : properFracRatio
)(x),
n = nr[0];
return 0 > nr[1] ? n - 1 : n;
};
// fst :: (a, b) -> a
const fst = ab =>
// First member of a pair.
ab[0];
// gcd :: Integral a => a -> a -> a
const gcd = x =>
y => {
const zero = x.constructor(0);
const go = (a, b) =>
zero === b ? (
a
) : go(b, a % b);
return go(abs(x), abs(y));
};
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
// A function defined by the right-to-left
// composition of all the functions in fs.
fs.reduce(
(f, g) => x => f(g(x)),
x => x
);
// iterate :: (a -> a) -> a -> Gen [a]
const iterate = f =>
// An infinite list of repeated
// applications of f to x.
function* (x) {
let v = x;
while (true) {
yield v;
v = f(v);
}
};
// last :: [a] -> a
const last = xs =>
// The last item of a list.
0 < xs.length ? (
xs.slice(-1)[0]
) : null;
// lcm :: Int -> Int -> Int
const lcm = x =>
// The smallest positive integer divisible
// without remainder by both x and y.
y => (x === 0 || y === 0) ? (
0
) : Math.abs(Math.floor(x / gcd(x)(y)) * y);
// mapAccumL :: (acc -> x -> (acc, y)) ->
// acc -> [x] -> (acc, [y])
const mapAccumL = f =>
// A tuple of an accumulation and a list
// obtained by a combined map and fold,
// with accumulation from left to right.
acc => xs => [...xs].reduce(
(a, x) => {
const ab = f(a[0])(x);
return [ab[0], a[1].concat(ab[1])];
},
[acc, []]
);
// maximum :: Ord a => [a] -> a
const maximum = xs => (
// The largest value in a non-empty list.
ys => 0 < ys.length ? (
ys.slice(1).reduce(
(a, y) => y > a ? (
y
) : a, ys[0]
)
) : undefined
)(xs);
// properFracRatio :: Ratio -> (Int, Ratio)
const properFracRatio = nd => {
const [q, r] = Array.from(quotRem(nd.n)(nd.d));
return Tuple(q)(Ratio(r)(nd.d));
};
// properFraction :: Real -> (Int, Real)
const properFraction = n => {
const i = Math.floor(n) + (n < 0 ? 1 : 0);
return Tuple(i)(n - i);
};
// quotRem :: Integral a => a -> a -> (a, a)
const quotRem = m =>
// The quotient, tupled with the remainder.
n => Tuple(
Math.trunc(m / n)
)(
m % n
);
// ratioDiv :: Rational -> Rational -> Rational
const ratioDiv = n1 => n2 => {
const [r1, r2] = [n1, n2].map(rational);
return Ratio(r1.n * r2.d)(
r1.d * r2.n
);
};
// ratioMinus :: Rational -> Rational -> Rational
const ratioMinus = n1 => n2 => {
const [r1, r2] = [n1, n2].map(rational);
const d = lcm(r1.d)(r2.d);
return Ratio(
(r1.n * (d / r1.d)) - (r2.n * (d / r2.d))
)(d);
};
// ratioMult :: Rational -> Rational -> Rational
const ratioMult = n1 => n2 => {
const [r1, r2] = [n1, n2].map(rational);
return Ratio(r1.n * r2.n)(
r1.d * r2.d
);
};
// ratioPlus :: Rational -> Rational -> Rational
const ratioPlus = n1 =>
n2 => {
const [r1, r2] = [n1, n2].map(rational);
const d = lcm(r1.d)(r2.d);
return Ratio(
(r1.n * (d / r1.d)) + (
r2.n * (d / r2.d)
)
)(d);
};
// rational :: Num a => a -> Rational
const rational = x =>
isNaN(x) ? x : Number.isInteger(x) ? (
Ratio(x)(1)
) : approxRatio(undefined)(x);
// replicateString :: Int -> String -> String
const replicateString = n =>
s => s.repeat(n);
// showRatio :: Ratio -> String
const showRatio = r =>
"Ratio" !== r.type ? (
r.toString()
) : r.n.toString() + (
1 !== r.d ? (
`/${r.d}`
) : ""
);
// signum :: Num -> Num
const signum = n =>
// | Sign of a number.
n.constructor(
0 > n ? (
-1
) : (
0 < n ? 1 : 0
)
);
// snd :: (a, b) -> b
const snd = ab =>
// Second member of a pair.
ab[1];
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n =>
// The first n elements of a list,
// string of characters, or stream.
xs => "GeneratorFunction" !== xs
.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat(...Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// unlines :: [String] -> String
const unlines = xs =>
// A single string formed by the intercalation
// of a list of strings with the newline character.
xs.join("\n");
// MAIN ---
return main();
})();
- Output:
(0, 1) (0, 1/3) (2/3, 1) (0, 1/9) (2/9, 1/3) (2/3, 7/9) (8/9, 1) (0, 1/27) (2/27, 1/9) (2/9, 7/27) (8/27, 1/3) (2/3, 19/27) (20/27, 7/9) (8/9, 25/27) (26/27, 1) ███████████████████████████ █████████ █████████ ███ ███ ███ ███ █ █ █ █ █ █ █ █
jq
# cantor(width; height)
def cantor($w; $h):
def init: [range(0; $h) | [range(0; $w) | "*"]];
def cantor($start; $leng; $ix):
($leng/3|floor) as $seg
| if $seg == 0 then .
else reduce range($ix; $h) as $i (.;
reduce range($start+$seg; $start + 2*$seg) as $j (.; .[$i][$j] = " "))
| cantor($start; $seg; $ix+1)
| cantor($start + 2*$seg; $seg; $ix+1)
end ;
init | cantor(0; $w; 1);
def pp: .[] | join("");
cantor($width; $height)
| pp
- Output:
With the above in a file, cantor.jq, the following incantation yields the same output as shown e.g. under `awk`, `julia`, etc.
jq -nr --argjson width 81 --argjson height 5 -f cantor.jq
Julia
const width = 81
const height = 5
function cantor!(lines, start, len, idx)
seg = div(len, 3)
if seg > 0
for i in idx+1:height, j in start + seg + 1: start + seg * 2
lines[i, j] = ' '
end
cantor!(lines, start, seg, idx + 1)
cantor!(lines, start + 2 * seg, seg, idx + 1)
end
end
lines = fill(UInt8('#'), height, width)
cantor!(lines, 0, width, 1)
for i in 1:height, j in 1:width
print(Char(lines[i, j]), j == width ? "\n" : "")
end
- Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
Kotlin
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:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Lua
local WIDTH = 81
local HEIGHT = 5
local lines = {}
function cantor(start, length, index)
-- must be local, or only one side will get calculated
local seg = math.floor(length / 3)
if 0 == seg then
return nil
end
-- remove elements that are not in the set
for it=0, HEIGHT - index do
i = index + it
for jt=0, seg - 1 do
j = start + seg + jt
pos = WIDTH * i + j
lines[pos] = ' '
end
end
-- left side
cantor(start, seg, index + 1)
-- right side
cantor(start + seg * 2, seg, index + 1)
return nil
end
-- initialize the lines
for i=0, WIDTH * HEIGHT do
lines[i] = '*'
end
-- calculate
cantor(0, WIDTH, 1)
-- print the result sets
for i=0, HEIGHT-1 do
beg = WIDTH * i
for j=beg, beg+WIDTH-1 do
if j <= WIDTH * HEIGHT then
io.write(lines[j])
end
end
print()
end
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Mathematica /Wolfram Language
Graphics[MeshPrimitives[CantorMesh[#],1]/.{x_}:>{x,-0.05#}&/@Range[5],ImageSize->600]
- Output:
A graphic of a Cantor set is shown
MiniScript
cantorSet = function(start, length, depth)
if depth == 0 then return [[start, start+length - 1]]
newLen = length / 3
leftInterval = cantorSet(start, newLen, depth - 1)
rightInterval = cantorSet(start + 2 * newLen, newLen, depth - 1)
return leftInterval + rightInterval
end function
for depth in range(0, 4)
output =[" "] * 81
segments = cantorSet(1, 81,depth)
for segment in segments
for x in range(segment[0] - 1, segment[1]-1)
output[x] = "#"
end for
end for
print output.join("")
end for
- Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
Modula-2
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.
Nim
import strutils
const
Width = 81
Height = 5
var lines: array[Height, string]
for line in lines.mitems: line = repeat('*', Width)
proc cantor(start, length, index: Natural) =
let seg = length div 3
if seg == 0: return
for i in index..<Height:
for j in (start + seg)..<(start + seg * 2):
lines[i][j] = ' '
cantor(start, seg, index + 1)
cantor(start + seg * 2, seg, index + 1)
cantor(0, Width, 1)
for line in lines:
echo line
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Objeck
class CantorSet {
WIDTH : static : Int;
HEIGHT : static : Int;
lines : static : Char[,];
function : Init() ~ Nil {
WIDTH := 81;
HEIGHT := 5;
lines := Char->New[HEIGHT, WIDTH];
each(i : HEIGHT) {
each(j : WIDTH) {
lines[i,j] := '*';
};
};
}
function : Cantor(start : Int, len : Int, index : Int) ~ Nil {
seg : Int := len / 3;
if(seg = 0) {
return;
};
for(i := index; i < HEIGHT; i += 1;) {
for(j := start + seg; j < start + seg * 2; j += 1;) {
lines[i,j] := ' ';
};
};
Cantor(start, seg, index + 1);
Cantor(start + seg * 2, seg, index + 1);
}
function : Main(args : String[]) ~ Nil {
Init();
Cantor(0, WIDTH, 1);
each(i : HEIGHT) {
each(j : WIDTH) {
lines[i,j]->Print();
};
""->PrintLine();
};
}
}
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Perl
use strict;
use feature 'say';
sub cantor {
our($height) = @_;
my $width = 3 ** ($height - 1);
our @lines = ('#' x $width) x $height;
sub trim_middle_third {
my($len, $start, $index) = @_;
my $seg = int $len / 3
or return;
for my $i ( $index .. $height - 1 ) {
for my $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:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
regex version
#!/usr/bin/perl -l
use strict; # https://rosettacode.org/wiki/Cantor_set
use warnings;
$_ = '#' x 81;
1 while print, s/(#+)\1\1/ $1 . $1 =~ tr!#! !r . $1 /ge;
- Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
Phix
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:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
Phixmonti
include ..\Utilitys.pmt
5 >ps
3 tps 1 - power var w
"#" 1 get nip w repeat var line
ps> for
3 swap 1 - power
w over / int var step
true >ps
for var j
tps not if
step for var k
line 32 j 1 - step * k + set var line
endfor
endif
ps> not >ps
endfor
cps
line ?
endfor
Other solution
include ..\Utilitys.pmt
5 >ps
3 tps 1 - power var w
"#" 1 get nip w repeat var line
( 2 ps> ) for
line ?
3 swap 1 - power
w over / int var step
2 swap 2 3 tolist for var j
step for var k
line 32 j 1 - step * k + set var line
endfor
endfor
endfor
line ?
- Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # # === Press any key to exit ===
PL/M
... under CP/M (or an emulator)
100H: /* DRAW A CANTOR SET USING ASCII */
/* BDOS SYSTEM CALL AND I/O ROUTINES */
BDOS: PROCEDURE( F, A ); DECLARE F BYTE, A ADDRESS; GOTO 5; END;
PR$CHAR: PROCEDURE( C ); DECLARE C BYTE; CALL BDOS( 2, C ); END;
PR$NL: PROCEDURE; CALL PR$CHAR( 0DH ); CALL PR$CHAR( 0AH ); END;
/* DRAW A CANTOR SET */
DECLARE LINES LITERALLY '4';
DECLARE WIDTH LITERALLY '27'; /* MUST BE 3**(LINES-1) */
DECLARE LINE (WIDTH)BYTE;
DECLARE ( I, L, C, W, S, SEGMENTS ) BYTE;
DO I = 0 TO LAST( LINE );
LINE( I ) = 023H;
CALL PR$CHAR( LINE( I ) );
END;
CALL PR$NL;
W = WIDTH;
SEGMENTS = 1;
DO L = 2 TO LINES;
W = W / 3;
SEGMENTS = SEGMENTS * 3;
C = 0;
DO S = 1 TO SEGMENTS;
DO I = 1 TO W;
IF NOT S THEN LINE( C ) = ' '; /* EVEN SEGMENT - BLANK IT */
CALL PR$CHAR( LINE( C ) );
C = C + 1;
END;
END;
CALL PR$NL;
END;
EOF
- Output:
########################### ######### ######### ### ### ### ### # # # # # # # #
Processing
//Aamrun, 1st July 2022
void cantorSet(int x1,int y1,int x2,int y2,int strWt,int gap,int n){
strokeWeight(strWt);
line(x1,y1,x2,y2);
if(n>0){
cantorSet(x1,gap + y1,(2*x1+x2)/3,gap + (2*y1+y2)/3,strWt,gap,n-1);
cantorSet((2*x2+x1)/3,gap + (2*y2+y1)/3,x2,gap + y2,strWt,gap,n-1);
}
}
void setup(){
size(1000,1000);
cantorSet(100,10,900,10,1,10,5);
}
Python
Imperative
WIDTH = 81
HEIGHT = 5
lines=[]
def cantor(start, len, index):
seg = len / 3
if seg == 0:
return None
for it in xrange(HEIGHT-index):
i = index + it
for jt in xrange(seg):
j = start + seg + jt
pos = i * WIDTH + j
lines[pos] = ' '
cantor(start, seg, index + 1)
cantor(start + seg * 2, seg, index + 1)
return None
lines = ['*'] * (WIDTH*HEIGHT)
cantor(0, WIDTH, 1)
for i in xrange(HEIGHT):
beg = WIDTH * i
print ''.join(lines[beg : beg+WIDTH])
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Functional
Separating (Bool, Int) model from String display:
'''Cantor set – separating model from display'''
from functools import (reduce)
import itertools
# cantor :: [(Bool, Int)] -> [(Bool, Int)]
def cantor(xs):
'''A Cantor segmentation step.'''
def go(tpl):
(bln, n) = tpl
m = n // 3
return [
(True, m), (False, m), (True, m)
] if bln and (1 < n) else [tpl]
return concatMap(go)(xs)
# cantorLines :: Int -> String
def cantorLines(n):
'''A text block display of n
Cantor-segmented lines.
'''
m = n - 1
repeat = itertools.repeat
return '\n'.join(
[showCantor(x) for x in (
reduce(
lambda a, f: a + [f(a[-1])],
repeat(cantor, m),
[[(True, 3 ** m)]]
)
)]
)
# showCantor :: [(Bool, Int)] -> String
def showCantor(xs):
'''A text block display of a list of
Cantor line segments.
'''
return ''.join(
concatMap(lambda tpl: tpl[1] * ('█' if tpl[0] else ' '))(
xs
)
)
# main :: IO ()
def main():
'''Testing to depth 5'''
print(
cantorLines(5)
)
# GENERIC -------------------------------------------------------------
# concatMap :: (a -> [b]) -> [a] -> [b]
def concatMap(f):
'''A concatenated list over which a function has been mapped.
The list monad can be derived by using a function f which
wraps its output in a list,
(using an empty list to represent computational failure).'''
chain = itertools.chain
return lambda xs: list(
chain.from_iterable(map(f, xs))
)
# MAIN ---
if __name__ == '__main__':
main()
- Output:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
Or, using strings for both model and display:
'''Cantor set – strings as both model and display.'''
from itertools import (chain, islice)
# cantorLines :: Int -> String
def cantorLines(n):
'''N levels of cantor segmentation,
obtained and displayed in the
form of lines of block characters.
'''
return '\n'.join(
[''.join(x) for x in islice(
iterate(cantor)(
[3 ** (n - 1) * '█']
), n
)]
)
# cantor :: [String] -> [String]
def cantor(xs):
'''A cantor line derived from its predecessor.'''
def go(s):
m = len(s) // 3
blocks = s[0:m]
return [
blocks, m * ' ', blocks
] if '█' == s[0] else [s]
return concatMap(go)(xs)
# MAIN ----------------------------------------------------
# main :: IO ()
def main():
'''Testing cantor line generation to level 5'''
print(
cantorLines(5)
)
# GENERIC -------------------------------------------------
# concatMap :: (a -> [b]) -> [a] -> [b]
def concatMap(f):
'''A concatenated list over which a function has been mapped.
The list monad can be derived by using a function f which
wraps its output in a list,
(using an empty list to represent computational failure).'''
return lambda xs: list(
chain.from_iterable(map(f, xs))
)
# iterate :: (a -> a) -> a -> Gen [a]
def iterate(f):
'''An infinite list of repeated
applications of f to x.
'''
def go(x):
v = x
while True:
yield v
v = f(v)
return lambda x: go(x)
# MAIN ---
if __name__ == '__main__':
main()
- Output:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
Dual representations – fractional and graphic
'''A Cantor set generator, and two different
representations of its output.
'''
from itertools import (islice, chain)
from fractions import Fraction
from functools import (reduce)
# ----------------------- CANTOR SET -----------------------
# cantor :: Generator [[(Fraction, Fraction)]]
def cantor():
'''A non-finite stream of successive Cantor
partitions of the line, in the form of
lists of fraction pairs.
'''
def go(xy):
(x, y) = xy
third = Fraction(y - x, 3)
return [(x, x + third), (y - third, y)]
return iterate(
concatMap(go)
)(
[(0, 1)]
)
# fractionLists :: [(Fraction, Fraction)] -> String
def fractionLists(xs):
'''A fraction pair representation of a
Cantor-partitioned line.
'''
def go(xy):
return ', '.join(map(showRatio, xy))
return ' '.join('(' + go(x) + ')' for x in xs)
# intervalBars :: [(Fraction, Fraction)] -> String
def intervalBars(w):
'''A block diagram representation of a
Cantor-partitioned line.
'''
def go(xs):
def show(a, tpl):
[x, y] = [int(w * r) for r in tpl]
return (
y,
(' ' * (x - a)) + ('█' * (y - x))
)
return mapAccumL(show)(0)(xs)
return lambda xs: ''.join(go(xs)[1])
# -------------------------- TEST --------------------------
# main :: IO ()
def main():
'''Testing the generation of successive
Cantor subdivisions of the line, and
displaying them both as lines of fraction
pairs and as graphic interval bars.
'''
xs = list(islice(cantor(), 4))
w = max(xy[1].denominator for xy in xs[-1])
print(
'\n'.join(map(fractionLists, xs)),
'\n'
)
print(
'\n'.join(map(intervalBars(w), xs))
)
# ------------------------ GENERIC -------------------------
# concatMap :: (a -> [b]) -> [a] -> [b]
def concatMap(f):
'''A concatenated list over which a function has been mapped.
The list monad can be derived by using a function f which
wraps its output in a list,
(using an empty list to represent computational failure).'''
return lambda xs: list(
chain.from_iterable(map(f, xs))
)
# iterate :: (a -> a) -> a -> Gen [a]
def iterate(f):
'''An infinite list of repeated
applications of f to x.
'''
def go(x):
v = x
while True:
yield v
v = f(v)
return go
# mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
def mapAccumL(f):
'''A tuple of an accumulation and a list derived by a
combined map and fold,
with accumulation from left to right.
'''
def go(a, x):
tpl = f(a[0], x)
return (tpl[0], a[1] + [tpl[1]])
return lambda acc: lambda xs: (
reduce(go, xs, (acc, []))
)
# showRatio :: Ratio -> String
def showRatio(r):
'''String representation of the ratio r.'''
d = r.denominator
return str(r.numerator) + (
'/' + str(d) if 1 != d else ''
)
# MAIN ---
if __name__ == '__main__':
main()
- Output:
(0, 1) (0, 1/3) (2/3, 1) (0, 1/9) (2/9, 1/3) (2/3, 7/9) (8/9, 1) (0, 1/27) (2/27, 1/9) (2/9, 7/27) (8/27, 1/3) (2/3, 19/27) (20/27, 7/9) (8/9, 25/27) (26/27, 1) ███████████████████████████ █████████ █████████ ███ ███ ███ ███ █ █ █ █ █ █ █ █
QB64
Note: Other languages will need to zero out the a() array. In QB64 all arrays are initialized to zero at program start.
_Title "Cantor Set"
Dim Shared As Integer sw, sh, wide, high
sw = 800: sh = 200: wide = 729: high = 7
Dim Shared As Integer a(wide, high)
Screen _NewImage(sw, sh, 8)
Cls , 15: Color 0
Call calc(0, wide, 1)
Call CantorSet
Sleep
System
Sub calc (start As Integer, length As Integer, index As Integer)
Dim As Integer i, j, newLength
newLength = length \ 3
If newLength = 0 Then Exit Sub
For j = index To high - 1
For i = start + newLength To start + newLength * 2 - 1
a(i, j) = 1
Next
Next
Call calc(start, newLength, index + 1)
Call calc(start + newLength * 2, newLength, index + 1)
End Sub
Sub CantorSet
Dim As Integer i, j, x, y
For y = 0 To high - 1
j = y + 1
For x = 0 To wide - 1
i = x + 34
If a(x, y) = 0 Then Line (i, j * 24 - 5)-(i, j * 24 + 17)
Next
Next
End Sub
Quackery
Using an L-System
(Described at L-system#Quackery.)
[ $ "" swap witheach
[ nested quackery join ] ] is expand ( $ --> $ )
[ $ "ABA" ] is A ( $ --> $ )
[ $ "BBB" ] is B ( $ --> $ )
[ char A = iff
[ char q ] else space
swap of echo$ ] is draw ( n c --> $ )
81 $ "A"
5 times
[ dup witheach
[ dip over draw ]
cr
i if
[ expand
dip [ 3 / ] ] ]
2drop
- Output:
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqq qqqqqqqqq qqqqqqqqq qqqqqqqqq qqq qqq qqq qqq qqq qqq qqq qqq q q q q q q q q q q q q q q q q
R
Attribution-NonCommercial-ShareAlike 4.0 International, CC BY-NC-SA 4.0 to neonira@gmail.com
cantorSet <- function() {
depth <- 6L
cs <- vector('list', depth)
cs[[1L]] <- c(0, 1)
for(k in seq_len(depth)) {
cs[[k + 1L]] <- unlist(sapply(seq_len(length(cs[[k]]) / 2L), function(j) {
p <- cs[[k]][2L] / 3
h <- 2L * (j - 1L)
c(
cs[[k]][h + 1L] + c(0, p),
cs[[k]][h + 2L] - c(p, 0)
)
}, simplify = FALSE))
}
cs
}
cantorSetGraph <- function() {
cs <- cantorSet()
u <- unlist(cs)
df <- data.frame(
x_start = u[seq_along(u) %% 2L == 1L],
x_end = u[seq_along(u) %% 2L == 0L],
depth = unlist(lapply(cs, function(e) {
l <- length(e)
n <- 0
while(l > 1) {
n <- n + 1L
l <- l / 2
}
rep(n, length(e) / 2)
}))
)
require(ggplot2)
g <- ggplot(df, aes_string(x = 'x_start', y = 'depth')) +
geom_segment(aes_string(xend = 'x_end', yend = 'depth', size = 3)) +
scale_y_continuous(trans = "reverse") +
theme(
axis.title = element_blank(),
axis.line = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
legend.position = 'none',
aspect.ratio = 1/5
)
list(graph = g, data = df, set = cs)
}
- Output:
Currently, RosettaCode does not seem to accept image upload anymore. So, you will have to run the program under R or RStudio IDE to get the diagram result.
Racket
#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:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Raku
(formerly Perl 6)
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:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
REXX
/*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
# 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:
Ruby
This works by numbering the segments (starting with 0) in base 3. Print whitespace if this number contains the digit 1; a black square otherwise.
lines = 5
(0..lines).each do |exp|
seg_size = 3**(lines-exp-1)
chars = (3**exp).times.map{ |n| n.digits(3).any?(1) ? " " : "█"}
puts chars.map{ |c| c * seg_size }.join
end
- Output:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
Rust
use convert_base::Convert;
use std::fmt;
struct CantorSet {
cells: Vec<Vec<bool>>,
}
fn number_to_vec(n: usize) -> Vec<u32> {
// for the conversion we need the digits in reverse order
// i.e the least significant digit in the first element of the vector
n.to_string()
.chars()
.rev()
.map(|c| c.to_digit(10).unwrap())
.collect()
}
impl CantorSet {
fn new(lines: usize) -> CantorSet {
// Convert from base 10- to base 3
let mut base = Convert::new(10, 3);
let mut cells: Vec<Vec<bool>> = vec![];
for line in 0..lines {
// calculate how many repeating sequence will be in the given line
let segment_size = 3_usize.pow((lines - line - 1) as u32);
let segment: Vec<bool> = (0..3_usize.pow(line as u32))
.map(|n| {
let output = base.convert::<u32, u32>(&number_to_vec(n));
// return false in case the base 3 number contains at least one "1"
// otherwise return true
!output.contains(&1)
})
.collect();
// copy the segment "segment_size" time
let mut accum: Vec<bool> = Vec::with_capacity(segment.len() * segment_size);
for c in segment.iter() {
accum.extend(std::iter::repeat(*c).take(segment_size))
}
cells.push(accum);
}
CantorSet { cells }
}
}
impl fmt::Display for CantorSet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for line in self.cells.iter() {
for c in line {
write!(f, "{}", if *c { "█" } else { " " })?
}
writeln!(f)?;
}
Ok(())
}
}
fn main() {
let cs = CantorSet::new(5);
println!("Cantor set:");
println!("{}", cs);
}
- Output:
Cantor set: █████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
Scala
Imperative Programming (Q&D)
object CantorSetQD extends App {
val (width, height) = (81, 5)
val lines = Seq.fill[Array[Char]](height)(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 height;
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)
object CantorSetFP extends App {
val (width, height) = (81, 5)
def lines = (1 to height).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 height)
yield if (index to height 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
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:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
Smalltalk
Smalltalk represents Intervals' start and stop values as Fraction, so precision is kept for quilte a while.
Object subclass: CantorSet [
| intervals |
CantorSet class >> new
[^self basicNew
initialize;
yourself]
initialize
[intervals := Array with: (CantorInterval
from: 0
to: 1)]
split
[intervals := intervals gather: [:each | each split]]
displayOn: aStream atScale: aNumber
[| current |
current := 0.
intervals do:
[:each |
(each start - current) * aNumber timesRepeat: [aStream space].
each length * aNumber timesRepeat: [aStream nextPut: $#].
current := each stop].
aStream nl]
]
Interval subclass: CantorInterval [
split
[| oneThird left right |
oneThird := self length / 3.
left := self class
from: start
to: start + oneThird.
right := self class
from: stop - oneThird
to: stop.
^Array
with: left
with: right]
start [^start]
stop [^stop]
length [^stop - start]
printOn: aStream
[aStream << ('%1[%2,%3]' % {self class name. start. stop})]
]
Object subclass: TestCantor [
TestCantor class >> iterations: anInteger
[| cantorset scale count |
scale := 3 raisedTo: anInteger. "Make smallest interval 1"
count := 0.
cantorset := CantorSet new.
[cantorset
displayOn: Transcript
atScale: scale.
count < anInteger] whileTrue:
[cantorset split.
count := count + 1]]
]
TestCantor iterations: 4.
Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
V (Vlang)
const (
width = 81
height = 5
)
fn cantor(mut lines [][]u8, start int, len int, index int) {
seg := len / 3
if seg == 0 {
return
}
for i in index.. height {
for j in start + seg..start + 2 * seg {
lines[i][j] = ' '[0]
}
}
cantor(mut lines, start, seg, index + 1)
cantor(mut lines, start + seg * 2, seg, index + 1)
}
fn main() {
mut lines := [][]u8{len:height, init: []u8{len:width, init:'*'[0]}}
cantor(mut lines, 0, width, 1)
for line in lines {
println(line.bytestr())
}
}
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
Visual Basic .NET
Module Module1
Const WIDTH = 81
Const HEIGHT = 5
Dim lines(HEIGHT, WIDTH) As Char
Sub Init()
For i = 0 To HEIGHT - 1
For j = 0 To WIDTH - 1
lines(i, j) = "*"
Next
Next
End Sub
Sub Cantor(start As Integer, len As Integer, index As Integer)
Dim seg As Integer = len / 3
If seg = 0 Then
Return
End If
For i = index To HEIGHT - 1
For j = start + seg To start + seg * 2 - 1
lines(i, j) = " "
Next
Next
Cantor(start, seg, index + 1)
Cantor(start + seg * 2, seg, index + 1)
End Sub
Sub Main()
Init()
Cantor(0, WIDTH, 1)
For i = 0 To HEIGHT - 1
For j = 0 To WIDTH - 1
Console.Write(lines(i, j))
Next
Console.WriteLine()
Next
End Sub
End Module
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
VTL-2
1010 L=4
1020 I=1
1030 X=1
1040 I=I+1
1050 X=X*3
1060 #=I<L*1040
2010 I=0
2020 :I)=35
2030 $=:I)
2040 I=I+1
2050 #=I<X*2020
2060 ?=""
2070 W=X
2080 G=1
2090 W=W/3
2100 G=G*3
2110 S=1
2120 C=0
2130 I=1
2140 #=S/2*0+%=1*2160
2150 :C)=32
2160 $=:C)
2170 C=C+1
2180 I=I+1
2190 #=W>I*2140
2200 S=S+1
2210 #=C<X*2130
2220 ?=""
2230 L=L-1
2240 #=1<L*2090
- Output:
########################### ######### ######### ### ### ### ### # # # # # # # #
Wren
var width = 81
var height = 5
var lines = [[]] * height
for (i in 0...height) lines[i] = ["*"] * width
var cantor // recursive so need to declare variable first
cantor = Fn.new { |start, len, index|
var seg = (len/3).floor
if (seg == 0) return
for (i in index...height) {
for (j in (start+seg)...(start+seg*2)) lines[i][j] = " "
}
cantor.call(start, seg, index + 1)
cantor.call(start + seg*2, seg, index + 1)
}
cantor.call(0, width, 1)
for (i in 0...height) System.print(lines[i].join())
- Output:
********************************************************************************* *************************** *************************** ********* ********* ********* ********* *** *** *** *** *** *** *** *** * * * * * * * * * * * * * * * *
XPL0
proc Cantor(N, LineSeg, Len); \Delete middle third of LineSeg
int N; char LineSeg; int Len, Third, I;
[if N>0 and Len>1 then
[Third:= Len/3;
for I:= Third, 2*Third-1 do LineSeg(I):= ^ ;
Cantor(N-1, LineSeg, Third);
Cantor(N-1, LineSeg+2*Third, Third);
];
];
char LineSeg, N;
[LineSeg:=
"#################################################################################
";
for N:= 0 to 4 do
[Cantor(N, LineSeg, 81);
Text(0, LineSeg);
];
]
- Output:
################################################################################# ########################### ########################### ######### ######### ######### ######### ### ### ### ### ### ### ### ### # # # # # # # # # # # # # # # #
zkl
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:
█████████████████████████████████████████████████████████████████████████████████ ███████████████████████████ ███████████████████████████ █████████ █████████ █████████ █████████ ███ ███ ███ ███ ███ ███ ███ ███ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
- Programming Tasks
- Solutions by Programming Task
- 11l
- Action!
- Ada
- ALGOL 68
- ALGOL W
- Amazing Hopper
- AppleScript
- Arturo
- AWK
- BASIC
- BASIC256
- Chipmunk Basic
- GW-BASIC
- MSX Basic
- QBasic
- True BASIC
- Yabasic
- BQN
- C
- C sharp
- C++
- CLU
- COBOL
- D
- Delphi
- EasyLang
- Elixir
- EMal
- Excel
- Factor
- Forth
- FreeBASIC
- FutureBasic
- Fōrmulæ
- Go
- Groovy
- Haskell
- IS-BASIC
- J
- Java
- JavaScript
- Jq
- Julia
- Kotlin
- Lua
- Mathematica
- Wolfram Language
- MiniScript
- Modula-2
- Nim
- Objeck
- Perl
- Phix
- Phixmonti
- PL/M
- Processing
- Python
- QB64
- Quackery
- R
- Racket
- Raku
- REXX
- Ring
- Ruby
- Rust
- Scala
- Sidef
- Smalltalk
- V (Vlang)
- Visual Basic .NET
- VTL-2
- Wren
- XPL0
- Zkl