75 lines
1.6 KiB
Python
75 lines
1.6 KiB
Python
'''Cantor set – separating model from display'''
|
||
|
||
from functools import (reduce)
|
||
import itertools
|
||
|
||
|
||
# cantor :: [(Bool, Int)] -> [(Bool, Int)]
|
||
def cantor(xs):
|
||
'''A Cantor segmentation step.'''
|
||
def go(tpl):
|
||
(bln, n) = tpl
|
||
m = n // 3
|
||
return [
|
||
(True, m), (False, m), (True, m)
|
||
] if bln and (1 < n) else [tpl]
|
||
return concatMap(go)(xs)
|
||
|
||
|
||
# cantorLines :: Int -> String
|
||
def cantorLines(n):
|
||
'''A text block display of n
|
||
Cantor-segmented lines.
|
||
'''
|
||
m = n - 1
|
||
repeat = itertools.repeat
|
||
return '\n'.join(
|
||
[showCantor(x) for x in (
|
||
reduce(
|
||
lambda a, f: a + [f(a[-1])],
|
||
repeat(cantor, m),
|
||
[[(True, 3 ** m)]]
|
||
)
|
||
)]
|
||
)
|
||
|
||
|
||
# showCantor :: [(Bool, Int)] -> String
|
||
def showCantor(xs):
|
||
'''A text block display of a list of
|
||
Cantor line segments.
|
||
'''
|
||
return ''.join(
|
||
concatMap(lambda tpl: tpl[1] * ('█' if tpl[0] else ' '))(
|
||
xs
|
||
)
|
||
)
|
||
|
||
|
||
# main :: IO ()
|
||
def main():
|
||
'''Testing to depth 5'''
|
||
|
||
print(
|
||
cantorLines(5)
|
||
)
|
||
|
||
|
||
# GENERIC -------------------------------------------------------------
|
||
|
||
# concatMap :: (a -> [b]) -> [a] -> [b]
|
||
def concatMap(f):
|
||
'''A concatenated list over which a function has been mapped.
|
||
The list monad can be derived by using a function f which
|
||
wraps its output in a list,
|
||
(using an empty list to represent computational failure).'''
|
||
chain = itertools.chain
|
||
return lambda xs: list(
|
||
chain.from_iterable(map(f, xs))
|
||
)
|
||
|
||
|
||
# MAIN ---
|
||
if __name__ == '__main__':
|
||
main()
|