RosettaCodeData/Task/Super-d-numbers/Python/super-d-numbers-2.py

140 lines
3.2 KiB
Python

'''Super-d numbers'''
from itertools import count, islice
from functools import reduce
# ------------------------ SUPER-D -------------------------
# super_d :: Int -> Either String [Int]
def super_d(d):
'''Either a message, if d is out of range, or
an infinite series of super_d numbers for d.
'''
if isinstance(d, int) and 1 < d < 10:
ds = d * str(d)
def p(x):
return ds in str(d * x ** d)
return Right(filter(p, count(2)))
else:
return Left(
'Super-d is defined only for integers drawn from {2..9}'
)
# ------------------------- TESTS --------------------------
# main :: IO ()
def main():
'''Attempted sampling of first 10 values for d <- [1..6],
where d = 1 is out of range.
'''
for v in map(
lambda x: either(
append(str(x) + ' :: ')
)(
compose(
append('First 10 super-' + str(x) + ': '),
showList
)
)(
bindLR(
super_d(x)
)(compose(Right, take(10)))
),
enumFromTo(1)(6)
): print(v)
# ------------------------ GENERIC -------------------------
# Left :: a -> Either a b
def Left(x):
'''Constructor for an empty Either (option type) value
with an associated string.
'''
return {'type': 'Either', 'Right': None, 'Left': x}
# Right :: b -> Either a b
def Right(x):
'''Constructor for a populated Either (option type) value'''
return {'type': 'Either', 'Left': None, 'Right': x}
# append (++) :: [a] -> [a] -> [a]
# append (++) :: String -> String -> String
def append(xs):
'''A list or string formed by
the concatenation of two others.
'''
def go(ys):
return xs + ys
return go
# bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
def bindLR(m):
'''Either monad injection operator.
Two computations sequentially composed,
with any value produced by the first
passed as an argument to the second.
'''
def go(mf):
return (
mf(m.get('Right')) if None is m.get('Left') else m
)
return go
# compose :: ((a -> a), ...) -> (a -> a)
def compose(*fs):
'''Composition, from right to left,
of a series of functions.
'''
def go(f, g):
def fg(x):
return f(g(x))
return fg
return reduce(go, fs, lambda x: x)
# either :: (a -> c) -> (b -> c) -> Either a b -> c
def either(fl):
'''The application of fl to e if e is a Left value,
or the application of fr to e if e is a Right value.
'''
return lambda fr: lambda e: fl(e['Left']) if (
None is e['Right']
) else fr(e['Right'])
# enumFromTo :: Int -> Int -> [Int]
def enumFromTo(m):
'''Enumeration of integer values [m..n]
'''
return lambda n: range(m, 1 + n)
# showList :: [a] -> String
def showList(xs):
'''Stringification of a list.'''
return '[' + ','.join(str(x) for x in xs) + ']'
# take :: Int -> [a] -> [a]
# take :: Int -> String -> String
def take(n):
'''The prefix of xs of length n,
or xs itself if n > length xs.
'''
def go(xs):
return islice(xs, n)
return go
# MAIN ---
if __name__ == '__main__':
main()