RosettaCodeData/Task/Matrix-transposition/Python/matrix-transposition-4.py

131 lines
3.3 KiB
Python

'''Transposition of row sets with possible gaps'''
from collections import defaultdict
# listTranspose :: [[a]] -> [[a]]
def listTranspose(xss):
'''Transposition of a matrix which may
contain gaps.
'''
def go(xss):
if xss:
h, *t = xss
return (
[[h[0]] + [xs[0] for xs in t if xs]] + (
go([h[1:]] + [xs[1:] for xs in t])
)
) if h and isinstance(h, list) else go(t)
else:
return []
return go(xss)
# TEST ----------------------------------------------------
# main :: IO ()
def main():
'''Tests with various lists of rows or non-row data.'''
def labelledList(kxs):
k, xs = kxs
return k + ': ' + showList(xs)
print(
fTable(
__doc__ + ':\n'
)(labelledList)(fmapFn(showList)(snd))(
fmapTuple(listTranspose)
)([
('Square', [[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
('Rectangle', [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),
('Rows with gaps', [[10, 11], [20], [], [31, 32, 33]]),
('Single row', [[1, 2, 3]]),
('Single row, one cell', [[1]]),
('Not rows', [1, 2, 3]),
('Nothing', [])
])
)
# TEST RESULT FORMATTING ----------------------------------
# fTable :: String -> (a -> String) ->
# (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
'''Heading -> x display function -> fx display function ->
f -> xs -> tabular string.
'''
def go(xShow, fxShow, f, xs):
ys = [xShow(x) for x in xs]
w = max(map(len, ys))
return s + '\n' + '\n'.join(map(
lambda x, y: y.rjust(w, ' ') + ' -> ' + fxShow(f(x)),
xs, ys
))
return lambda xShow: lambda fxShow: lambda f: lambda xs: go(
xShow, fxShow, f, xs
)
# fmapFn :: (a -> b) -> (r -> a) -> r -> b
def fmapFn(f):
'''The application of f to the result of g.
fmap over a function is composition.
'''
return lambda g: lambda x: f(g(x))
# fmapTuple :: (a -> b) -> (c, a) -> (c, b)
def fmapTuple(f):
'''A pair in which f has been
applied to the second item.
'''
return lambda ab: (ab[0], f(ab[1])) if (
2 == len(ab)
) else None
# show :: a -> String
def show(x):
'''Stringification of a value.'''
def go(v):
return defaultdict(lambda: repr, [
('list', showList)
# ('Either', showLR),
# ('Maybe', showMaybe),
# ('Tree', drawTree)
])[
typeName(v)
](v)
return go(x)
# showList :: [a] -> String
def showList(xs):
'''Stringification of a list.'''
return '[' + ','.join(show(x) for x in xs) + ']'
# snd :: (a, b) -> b
def snd(tpl):
'''Second member of a pair.'''
return tpl[1]
# typeName :: a -> String
def typeName(x):
'''Name string for a built-in or user-defined type.
Selector for type-specific instances
of polymorphic functions.
'''
if isinstance(x, dict):
return x.get('type') if 'type' in x else 'dict'
else:
return 'iter' if hasattr(x, '__next__') else (
type(x).__name__
)
# MAIN ---
if __name__ == '__main__':
main()