RosettaCodeData/Task/Cantor-set/Python/cantor-set-2.py

75 lines
1.6 KiB
Python
Raw Permalink Normal View History

2023-07-01 11:58:00 -04:00
'''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()