131 lines
3.3 KiB
Python
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()
|