Cantor set

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

Draw Cantor set. See details: Cantor set


ALGOL 68[edit]

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

ALGOL W[edit]

Based on the Algol 68 sample.

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

AppleScript[edit]

-- 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
Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

AWK[edit]

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

C[edit]

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

C++[edit]

Translation of: D
#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:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

C#[edit]

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

D[edit]

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

Factor[edit]

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

Go[edit]

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

Haskell[edit]

Interval bars[edit]

Translation of: Python
(Functional version)
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
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

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

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
Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Dual representation[edit]

Intervals as fraction pairs, and intervals as graphic bars:

import Data.Ratio (Ratio, (%), numerator, denominator)
import Data.List (intercalate, mapAccumL, maximumBy)
 
cantor :: (Rational, Rational) -> [[(Rational, Rational)]]
cantor = iterate go . return
where
go xs =
xs >>=
\(x, y) ->
let third = (y - x) / 3
in [(x, x + third), (y - third, y)]
 
main :: IO ()
main = do
let xs = take 4 $ cantor (0, 1)
(putStrLn . unlines) $ intervalRatios <$> xs
putStrLn $ intervalBars xs
 
-- DISPLAY FUNCTIONS ---------------------------------------------------
 
intervalBars :: [[(Rational, Rational)]] -> String
intervalBars xs =
let go w xs =
concat . snd $
mapAccumL
(\a (rx, ry) ->
let (wx, wy) = (w * rx, w * ry)
in ( wy -- Accumulator – end of previous interval.
, replicate (floor (wx - a)) ' ' -- Preceding gap, and
++
replicate (floor (wy - wx)) '█' -- interval bar.
))
0
xs
d = maximum $ (denominator . fst) <$> last xs
in unlines $ go (d % 1) <$> xs
 
intervalRatios :: [(Rational, Rational)] -> String
intervalRatios xs =
let go (rx, ry) = intercalate ", " $ showRatio <$> [rx, ry]
in "(" ++ intercalate ") (" (go <$> xs) ++ ")"
 
showRatio :: Rational -> String
showRatio r =
let d = denominator r
in show (numerator r) ++
(if 1 /= d
then '/' : show (denominator r)
else [])
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)

███████████████████████████
█████████         █████████
███   ███         ███   ███
█ █   █ █         █ █   █ █

Java[edit]

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

JavaScript[edit]

Translation of: Python
(Functional version)
Translation of: Haskell
(() => {
'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();
})();
Output:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

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

Translation of: Haskell
(() => {
'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();
})();
Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Dual representation[edit]

Translation of: Haskell
Translation of: Python


Cantor ternary intervals rendered both as lists of ratio 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';
 
// main :: IO ()
const main = () => {
 
const xs = take(4, iterate(cantor, [Tuple(0, 1)]));
 
console.log(
unlines(map(intervalRatios, xs)) + '\n',
);
console.log(
intervalBars(xs)
);
};
 
// cantor :: [(Rational, Rational)] -> [(Rational, Rational)]
const cantor = xs => {
const go = tpl => {
const [r1, r2] = map(rational, Array.from(tpl));
const third = ratioDiv(ratioMinus(r2, r1), 3);
return [
Tuple(r1, ratioPlus(r1, third)),
Tuple(ratioMinus(r2, third), r2)
];
};
return concatMap(go, xs);
};
 
// intervalRatios :: [(Rational, Rational)] -> String
const intervalRatios = xs => {
const go = tpl =>
map(compose(showRatio, rational),
Array.from(tpl)
).join(', ');
return '(' + map(go, xs).join(') (') + ')';
};
 
// intervalBars :: [[(Rational, Rational)]] -> String
const intervalBars = xs => {
const go = w => xs =>
concat(snd(mapAccumL(
(a, tpl) => {
const [wx, wy] = map(
r => ratioMult(w, rational(r)),
Array.from(tpl)
);
return Tuple(
wy,
replicateString(floor(ratioMinus(wx, a)), ' ') +
replicateString(floor(ratioMinus(wy, wx)), '█')
);
},
0, xs
)));
const d = maximum(map(x => fst(x).d, last(xs)));
return unlines(map(go(ratio(d, 1)), xs));
};
 
 
// GENERIC FUNCTIONS ----------------------------------
 
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
 
// abs :: Num -> Num
const abs = Math.abs;
 
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (f, g) => x => f(g(x));
 
// 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)), []);
 
// floor :: Num -> Int
const floor = x => {
const
nr = (
'Ratio' !== x.type ? (
properFraction
) : properFracRatio
)(x),
n = nr[0];
return 0 > nr[1] ? n - 1 : n;
};
 
// foldl1 :: (a -> a -> a) -> [a] -> a
const foldl1 = (f, xs) =>
1 < xs.length ? xs.slice(1)
.reduce(f, xs[0]) : xs[0];
 
// fst :: (a, b) -> a
const fst = tpl => tpl[0];
 
// gcd :: Int -> Int -> Int
const gcd = (x, y) => {
const
_gcd = (a, b) => (0 === b ? a : _gcd(b, a % b)),
abs = Math.abs;
return _gcd(abs(x), abs(y));
};
 
// iterate :: (a -> a) -> a -> Gen [a]
function* iterate(f, x) {
let v = x;
while (true) {
yield(v);
v = f(v);
}
}
 
// last :: [a] -> a
const last = xs =>
0 < xs.length ? xs.slice(-1)[0] : undefined;
 
// lcm :: Int -> Int -> Int
const lcm = (x, y) =>
(x === 0 || y === 0) ? 0 : Math.abs(Math.floor(x / gcd(x, y)) * y);
 
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
 
// 'The mapAccumL function behaves like a combination of map and foldl;
// it applies a function to each element of a list, passing an accumulating
// parameter from left to right, and returning a final value of this
// accumulator together with the new list.' (See Hoogle)
 
// mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
const mapAccumL = (f, acc, xs) =>
xs.reduce((a, x, i) => {
const pair = f(a[0], x, i);
return Tuple(pair[0], a[1].concat(pair[1]));
}, Tuple(acc, []));
 
// maximum :: Ord a => [a] -> a
const maximum = xs =>
0 < xs.length ? (
foldl1((a, x) => x > a ? x : a, xs)
) : undefined;
 
// 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);
};
 
// quot :: Int -> Int -> Int
const quot = (n, m) => Math.floor(n / m);
 
// quotRem :: Int -> Int -> (Int, Int)
const quotRem = (m, n) =>
Tuple(Math.floor(m / n), m % n);
 
// ratio :: Int -> Int -> Ratio Int
const ratio = (x, y) => {
const go = (x, y) =>
0 !== y ? (() => {
const d = gcd(x, y);
return {
type: 'Ratio',
'n': quot(x, d), // numerator
'd': quot(y, d) // denominator
};
})() : undefined;
return go(x * signum(y), abs(y));
};
 
// ratioDiv :: Rational -> Rational -> Rational
const ratioDiv = (n1, n2) => {
const [r1, r2] = map(rational, [n1, n2]);
return ratio(r1.n * r2.d, r1.d * r2.n);
};
 
// ratioMinus :: Rational -> Rational -> Rational
const ratioMinus = (n1, n2) => {
const [r1, r2] = map(rational, [n1, n2]);
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] = map(rational, [n1, n2]);
return ratio(r1.n * r2.n, r1.d * r2.d);
};
 
// ratioPlus :: Rational -> Rational -> Rational
const ratioPlus = (n1, n2) => {
const [r1, r2] = map(rational, [n1, n2]);
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 : ratio(x, 1);
 
// 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.toString()
) : ''
);
 
// signum :: Num -> Num
const signum = n => 0 > n ? -1 : (0 < n ? 1 : 0);
 
// snd :: (a, b) -> b
const snd = tpl => tpl[1];
 
// 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();
})();
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)

███████████████████████████
█████████         █████████
███   ███         ███   ███
█ █   █ █         █ █   █ █

Kotlin[edit]

Simple terminal drawing.

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

Lua[edit]

Translation of: python
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:
*********************************************************************************
***************************                           ***************************
*********         *********                           *********         *********
***   ***         ***   ***                           ***   ***         ***   ***
* *   * *         * *   * *                           * *   * *         * *   * *

Modula-2[edit]

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

Perl[edit]

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

Perl 6[edit]

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

Phix[edit]

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

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

Python[edit]

Imperative[edit]

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[edit]

Separating (Bool, Int) model from String display:

from itertools import (chain, repeat)
from functools import (reduce)
 
 
# 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)
 
 
# 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)]]
)
)]
)
 
 
# showCantor :: [(Bool, Int)] -> String
def showCantor(xs):
return ''.join(
concatMap(lambda tpl: tpl[1] * ('█' if tpl[0] else ' '))(
xs
)
)
 
 
# main :: IO ()
def main():
print (
cantorLines(5)
)
 
 
# GENERIC -------------------------------------------------------------
 
 
# concatMap :: (a -> [b]) -> [a] -> [b]
def concatMap(f):
return lambda xs: list(
chain.from_iterable(
map(f, xs)
)
)
 
 
if __name__ == '__main__':
main()
Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Or, using strings for both model and display:

Translation of: JavaScript
Translation of: Haskell
from itertools import (chain, islice)
 
 
# main :: IO ()
def main():
print (
cantorLines(5)
)
 
 
# cantorLines :: Int -> String
def cantorLines(n):
return '\n'.join(
[''.join(x) for x in islice(
iterate(cantor)(
[3 ** (n - 1) * '█']
), n
)]
)
 
 
# 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)
 
 
# GENERIC -------------------------------------------------
 
 
# concatMap :: (a -> [b]) -> [a] -> [b]
def concatMap(f):
return lambda xs: list(
chain.from_iterable(
map(f, xs)
)
)
 
 
# 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)
 
 
# MAIN ---
main()
Output:
█████████████████████████████████████████████████████████████████████████████████
███████████████████████████                           ███████████████████████████
█████████         █████████                           █████████         █████████
███   ███         ███   ███                           ███   ███         ███   ███
█ █   █ █         █ █   █ █                           █ █   █ █         █ █   █ █

Dual representations – fractional and graphic[edit]

from itertools import (islice, chain)
from functools import (reduce)
from fractions import Fraction
 
 
# 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 = [(0, 1)]
while True:
yield xs
xs = concatMap(go)(xs)
 
 
# 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)),
'\n'
)
print(
'\n'.join(map(intervalBars(w), xs))
)
 
 
# fractionLists :: [(Fraction, Fraction)] -> String
def fractionLists(xs):
def go(xy):
return ', '.join(map(showRatio, xy))
return ' '.join('(' + go(x) + ')' for x in xs)
 
 
# 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)) # A gap + an interval bar
)
return mapAccumL(show)(0)(xs)
return lambda xs: ''.join(go(xs)[1])
 
 
# GENERIC -----------------------------------------------------
 
# concatMap :: (a -> [b]) -> [a] -> [b]
def concatMap(f):
return lambda xs: list(
chain.from_iterable(
map(f, xs)
)
)
 
 
# 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, []))
)
 
 
# showRatio :: Ratio -> String
def showRatio(r):
d = r.denominator
return str(r.numerator) + (
'/' + str(d) if 1 != d else ''
)
 
 
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) 

███████████████████████████
█████████         █████████
███   ███         ███   ███
█ █   █ █         █ █   █ █

Racket[edit]

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

REXX[edit]

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

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

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

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

(Shown at half size.)

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

(Shown at half size.)

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

Ring[edit]

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

Output image:

Cantor set

Scala[edit]

Imperative Programming (Q&D)[edit]

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

or by Scastie (JVM).

Functional Programming (Recommended)[edit]

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

or by Scastie (JVM).

Sidef[edit]

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

zkl[edit]

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