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

<lang algol68>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</lang>

Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

ALGOL W

Based on the Algol 68 sample. <lang algolw>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.</lang>

Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

AppleScript

<lang applescript>-- 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


-- showCantor :: Int -> String on showCantor(n)

   unlines(map(my concat, ¬
       take(n, iterate(cantor, ¬
           {replicate(3 ^ (n - 1), "█")}))))

end showCantor


-- TEST --------------------------------------------------- on run

   showCantor(5)

end run


-- GENERIC ------------------------------------------------

-- 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</lang>

Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

AWK

<lang AWK>

  1. syntax: GAWK -f CANTOR_SET.AWK
  2. 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)

} </lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

C

Translation of: Kotlin

<lang c>#include <stdio.h>

  1. define WIDTH 81
  2. 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;

}</lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

C#

Translation of: Java

<lang csharp>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();
           }
       }
   }

}</lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

D

Translation of: C

<lang 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]);
   }

}</lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Factor

<lang 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</lang>

Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

Go

Translation of: Kotlin

<lang 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[:]))
   }

}</lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Haskell

Translation of: Python

(Functional version)

<lang haskell>cantor :: [(Bool, Int)] -> [(Bool, Int)] cantor xs =

 let go (bln, n)
       | bln && 1 < n = [(True, m), (False, m), (True, m)]
       | otherwise = [(bln, n)]
       where
         m = quot n 3
 in xs >>= go

cantorLines :: Int -> String cantorLines n =

 unlines $ showCantor <$> take n (iterate cantor [(True, 3 ^ (n - 1))])

showCantor :: [(Bool, Int)] -> String showCantor =

 concatMap
   (\(bln, n) ->
       replicate
         n
         (if bln
            then '*'
            else ' '))

main :: IO () main = putStrLn $ cantorLines 5</lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Or, using strings for the model as well as the display:

<lang haskell>cantor :: [String] -> [String] cantor xs =

 let go x
       | ('█' == head x) = [block, gap, block]
       | otherwise = [x]
       where
         m = quot (length x) 3
         gap = replicate m ' '
         block = take m x
 in xs >>= go

cantorLines :: Int -> String cantorLines n =

 unlines $ concat <$> take n (iterate cantor [replicate (3 ^ (n - 1)) '█'])

main :: IO () main = putStrLn $ cantorLines 5</lang>

Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Java

Translation of: Kotlin

<lang 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();
       }
   }

} </lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

JavaScript

Translation of: Python

(Functional version)

Translation of: Haskell

<lang JavaScript>(() => {

   'use strict';
   const main = () => {
       // cantor :: [(Bool, Int)] -> [(Bool, Int)]
       const cantor = xs => {
           const go = ([bln, n]) =>
               bln && 1 < n ? (() => {
                   const lng = Math.floor(n / 3);
                   return [
                       [true, lng],
                       [false, lng],
                       [true, lng]
                   ]
               })() : [
                   [bln, n]
               ];
           return concatMap(go, xs);
       };
       // cantorLines :: Int -> String
       const cantorLines = n =>
           unlines(
               map(showCantor,
                   take(n,
                       iterate(
                           cantor,
                           [
                               [true, Math.pow(3, n - 1)]
                           ]
                       )
                   )
               )
           );
       // showCantor :: [(Bool, Int)] -> String
       const showCantor = xs =>
           concat(map(
               ([bln, n]) => replicate(n, bln ? '*' : ' '), xs
           ));
       console.log(
           cantorLines(5)
       );
   };
   // GENERIC FUNCTIONS ----------------------------------
   // concat :: a -> [a]
   // concat :: [String] -> String
   const concat = xs =>
       0 < xs.length ? (() => {
           const unit = 'string' !== typeof xs[0] ? (
               []
           ) : ;
           return unit.concat.apply(unit, xs);
       })() : [];
   // concatMap :: (a -> [b]) -> [a] -> [b]
   const concatMap = (f, xs) =>
       xs.reduce((a, x) => a.concat(f(x)), []);
   // iterate :: (a -> a) -> a -> Gen [a]
   function* iterate(f, x) {
       let v = x;
       while (true) {
           yield(v);
           v = f(v);
       }
   }
   // map :: (a -> b) -> [a] -> [b]
   const map = (f, xs) => xs.map(f);
   // replicate :: Int -> String -> String
   const replicate = (n, s) => s.repeat(n);
   // take :: Int -> [a] -> [a]
   // take :: Int -> String -> String
   const take = (n, xs) =>
       'GeneratorFunction' !== xs.constructor.constructor.name ? (
           xs.slice(0, n)
       ) : [].concat.apply([], Array.from({
           length: n
       }, () => {
           const x = xs.next();
           return x.done ? [] : [x.value];
       }));
   // unlines :: [String] -> String
   const unlines = xs => xs.join('\n');
   // MAIN ---
   return main();

})();</lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Or, using strings for the model as well as the display:

Translation of: Haskell

<lang javascript>(() => {

   'use strict';
   const main = () => showCantor(5);
   // showCantor :: Int -> String
   const showCantor = n =>
       unlines(map(concat,
           take(n,
               iterate(
                   cantor,
                   [replicate(Math.pow(3, n - 1), '█')]
               )
           )
       ));
   // cantor :: [String] -> [String]
   const cantor = xs => {
       const go = s => {
           const
               m = Math.floor(s.length / 3),
               blocks = take(m, s);
           return '█' === s[0] ? (
               [blocks, replicate(m, ' '), blocks]
           ) : [s];
       };
       return concatMap(go, xs);
   };
   // GENERIC FUNCTIONS ----------------------------
   // concat :: a -> [a]
   // concat :: [String] -> String
   const concat = xs =>
       0 < xs.length ? (() => {
           const unit = 'string' !== typeof xs[0] ? (
               []
           ) : ;
           return unit.concat.apply(unit, xs);
       })() : [];
   // concatMap :: (a -> [b]) -> [a] -> [b]
   const concatMap = (f, xs) =>
       xs.reduce((a, x) => a.concat(f(x)), []);
   // iterate :: (a -> a) -> a -> Gen [a]
   function* iterate(f, x) {
       let v = x;
       while (true) {
           yield(v);
           v = f(v);
       }
   }
   // map :: (a -> b) -> [a] -> [b]
   const map = (f, xs) => xs.map(f);
   // replicate :: Int -> String -> String
   const replicate = (n, s) => s.repeat(n);
   // take :: Int -> [a] -> [a]
   // take :: Int -> String -> String
   const take = (n, xs) =>
       'GeneratorFunction' !== xs.constructor.constructor.name ? (
           xs.slice(0, n)
       ) : [].concat.apply([], Array.from({
           length: n
       }, () => {
           const x = xs.next();
           return x.done ? [] : [x.value];
       }));
   // unlines :: [String] -> String
   const unlines = xs => xs.join('\n');
   // MAIN ---
   return main();

})();</lang>

Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Kotlin

Simple terminal drawing. <lang scala>// 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) }

}</lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Lua

Translation of: python

<lang 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</lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Modula-2

Translation of: Kotlin

<lang modula2>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.</lang>

Perl

Translation of: Perl 6

<lang Perl>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);</lang>

Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

Perl 6

Translation of: Kotlin

<lang perl6>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);</lang>

Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Phix

Based on Algol 68, but even simpler, shorter, and sweeter! <lang Phix>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</lang>

Output:
#################################################################################
###########################                           ###########################
#########         #########                           #########         #########
###   ###         ###   ###                           ###   ###         ###   ###
# #   # #         # #   # #                           # #   # #         # #   # #

Python

Imperative

<lang python>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])</lang>
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Functional

Separating (Bool, Int) model from String display: <lang python>from itertools import (chain, repeat) from functools import (reduce)


  1. cantor :: [(Bool, Int)] -> [(Bool, Int)]

def cantor(xs):

   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)


  1. cantorLines :: Int -> String

def cantorLines(n):

   m = n - 1
   return '\n'.join(
       [showCantor(x) for x in (
           reduce(
               lambda a, f: a + [f(a[-1])],
               repeat(cantor, m),
               (True, 3 ** m)
           )
       )]
   )


  1. showCantor :: [(Bool, Int)] -> String

def showCantor(xs):

   return .join(
       concatMap(lambda tpl: tpl[1] * ('█' if tpl[0] else ' '))(
           xs
       )
   )


  1. main :: IO ()

def main():

   print (
       cantorLines(5)
   )


  1. GENERIC -------------------------------------------------------------


  1. concatMap :: (a -> [b]) -> [a] -> [b]

def concatMap(f):

   return lambda xs: list(
       chain.from_iterable(
           map(f, xs)
       )
   )


if __name__ == '__main__':

   main()</lang>
Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Or, using strings for both model and display:

Translation of: JavaScript
Translation of: Haskell

<lang python>from itertools import (chain, islice)


  1. main :: IO ()

def main():

   print (
       cantorLines(5)
   )


  1. cantorLines :: Int -> String

def cantorLines(n):

   return '\n'.join(
       [.join(x) for x in islice(
           iterate(cantor)(
               [3 ** (n - 1) * '█']
           ), n
       )]
   )


  1. cantor :: [String] -> [String]

def cantor(xs):

   def go(s):
       m = len(s) // 3
       blocks = s[0:m]
       return [
           blocks, m * ' ', blocks
       ] if '█' == s[0] else [s]
   return concatMap(go)(xs)


  1. GENERIC -------------------------------------------------


  1. concatMap :: (a -> [b]) -> [a] -> [b]

def concatMap(f):

   return lambda xs: list(
       chain.from_iterable(
           map(f, xs)
       )
   )


  1. iterate :: (a -> a) -> a -> Gen [a]

def iterate(f):

   def go(x):
       v = x
       while True:
           yield(v)
           v = f(v)
   return lambda x: go(x)


  1. MAIN ---

main()</lang>

Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Dual representations – fractional and graphic

<lang python>from itertools import (islice, chain) from functools import (reduce) from fractions import Fraction


  1. cantor :: Generator (Fraction, Fraction)

def cantor():

   def go(xy):
       (x, y) = xy
       third = Fraction(y - x, 3)
       return [(x, x + third), (y - third, y)]
   xs = [(Fraction(0), Fraction(1))]
   while True:
       yield xs
       xs = concatMap(go)(xs)


  1. main :: IO ()

def main():

   xs = list(islice(cantor(), 4))
   w = max(xy[1].denominator for xy in xs[-1])
   print(
       '\n'.join(map(fractionLists, xs))
   )
   print(
       '\n'.join(map(intervalBars(w), xs))
   )


  1. fractionLists :: [(Fraction, Fraction)] -> String

def fractionLists(xs):

   def go(xy):
       return ', '.join([
           (str(r.numerator) + '/' + str(r.denominator) if (
               r.denominator != 1
           ) else str(r.numerator)) for r in xy
       ])
   return ' '.join('[' + go(x) + ']' for x in xs)


  1. intervalBars :: [(Fraction, Fraction)] -> String

def intervalBars(w):

   def go(xs):
       def show(a, tpl):
           [x, y] = [int(w * r) for r in tpl]
           return (
               y,  # Accumulator - end of previous interval
               (' ' * (x - a)) + ('█' * (y - x))  # Gap + interval as string
           )
       return mapAccumL(show)(0)(
           [x for x in [xy for xy in xs]]
       )
   return lambda xs: .join(go(xs)[1])


  1. GENERIC -----------------------------------------------------
  1. concatMap :: (a -> [b]) -> [a] -> [b]

def concatMap(f):

   return lambda xs: list(
       chain.from_iterable(
           map(f, xs)
       )
   )


  1. mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])

def mapAccumL(f):

   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, []))
   )


main()</lang>

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]
███████████████████████████
█████████         █████████
███   ███         ███   ███
█ █   █ █         █ █   █ █

Racket

Translation of: Kotlin

<lang 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)))

</lang>

Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

REXX

<lang 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. */</lang>

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

<lang ring>

  1. 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

</lang> Output image:

Cantor set

Scala

Imperative Programming (Q&D)

<lang Scala>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))

}</lang>

Output:

See it in running in your browser by (JavaScript)

or by Scastie (JVM).

Functional Programming (Recommended)

<lang Scala>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"))

}</lang>

Output:

See it in running in your browser by (JavaScript)

or by Scastie (JVM).

Sidef

Translation of: Perl 6

<lang ruby>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 }</lang>

Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

zkl

<lang 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");</lang>

Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █