RosettaCodeData/Task/Pascal-matrix-generation/Python/pascal-matrix-generation-3.py

137 lines
3.0 KiB
Python

'''Pascal matrix generation'''
from functools import reduce
from itertools import chain
from operator import add
# pascalMatrix :: Int -> ((Int, Int) -> (Int, Int)) -> [[Int]]
def pascalMatrix(n):
'''Pascal S-, L-, or U- matrix of order n.
'''
return lambda f: chunksOf(n)(list(map(
compose(binomialCoefficent, f),
tupleRange((0, 0), (n, n))
)))
# binomialCoefficent :: (Int, Int) -> Int
def binomialCoefficent(nk):
'''The binomial coefficient of the tuple (n, k).
'''
n, k = nk
def go(a, x):
return a * (n - x + 1) // x
return reduce(go, enumFromTo(1)(k), 1)
# --------------------------TEST---------------------------
# main :: IO ()
def main():
'''Pascal S-, L-, and U- matrices of order 5.
'''
order = 5
for k, f in [
('Symmetric', lambda ab: (add(*ab), ab[1])),
('Lower', identity),
('Upper', swap)
]:
print(k + ':')
print(showMatrix(
pascalMatrix(order)(f)
))
print()
# --------------------REUSABLE GENERICS--------------------
# chunksOf :: Int -> [a] -> [[a]]
def chunksOf(n):
'''A series of lists of length n, subdividing the
contents of xs. Where the length of xs is not evenly
divible, the final list will be shorter than n.
'''
return lambda xs: reduce(
lambda a, i: a + [xs[i:n + i]],
range(0, len(xs), n), []
) if 0 < n else []
# compose :: ((a -> a), ...) -> (a -> a)
def compose(*fs):
'''Composition, from right to left,
of a series of functions.
'''
return lambda x: reduce(
lambda a, f: f(a),
fs[::-1], x
)
# enumFromTo :: Int -> Int -> [Int]
def enumFromTo(m):
'''Enumeration of integer values [m..n]'''
return lambda n: range(m, 1 + n)
# identity :: a -> a
def identity(x):
'''The identity function.'''
return x
# showMatrix :: [[Int]] -> String
def showMatrix(xs):
'''String representation of xs
as a matrix.
'''
def go():
rows = [[str(x) for x in row] for row in xs]
w = max(map(len, chain.from_iterable(rows)))
return unlines(
unwords(k.rjust(w, ' ') for k in row)
for row in rows
)
return go() if xs else ''
# swap :: (a, b) -> (b, a)
def swap(tpl):
'''The swapped components of a pair.'''
return tpl[1], tpl[0]
# tupleRange :: (Int, Int) -> (Int, Int) -> [(Int, Int)]
def tupleRange(lowerTuple, upperTuple):
'''Range of (Int, Int) tuples from
lowerTuple to upperTuple.
'''
l1, l2 = lowerTuple
u1, u2 = upperTuple
return [
(i1, i2) for i1 in range(l1, u1)
for i2 in range(l2, u2)
]
# unlines :: [String] -> String
def unlines(xs):
'''A single string formed by the intercalation
of a list of strings with the newline character.
'''
return '\n'.join(xs)
# unwords :: [String] -> String
def unwords(xs):
'''A space-separated string derived
from a list of words.
'''
return ' '.join(xs)
# MAIN ---
if __name__ == '__main__':
main()