140 lines
3.2 KiB
Python
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()
|