Functional coverage tree: Difference between revisions
Content added Content deleted
(Added Wren) |
m (→Python: Composition of pure functions: Tidied, updated primitives.) |
||
Line 2,392: | Line 2,392: | ||
delimiter = '|' |
delimiter = '|' |
||
reportLines = |
reportLines = REPORT.splitlines() |
||
columnTitles = init(columnNames(delimiter)(reportLines[0])) |
columnTitles = init(columnNames(delimiter)(reportLines[0])) |
||
# SERIALISATION OF DECORATED PARSE TREE |
# ------ SERIALISATION OF DECORATED PARSE TREE ------- |
||
print(titleLine(delimiter)(columnWidths)( |
print(titleLine(delimiter)(columnWidths)( |
||
columnTitles + ['share of residue'] |
columnTitles + ['share of residue'] |
||
Line 2,401: | Line 2,401: | ||
print(indentedLinesFromTree(' ', tabulation(columnWidths))( |
print(indentedLinesFromTree(' ', tabulation(columnWidths))( |
||
# TWO COMPUTATIONS BY TRAVERSAL |
# -------- TWO COMPUTATIONS BY TRAVERSAL --------- |
||
withResidueShares(1.0)( |
withResidueShares(1.0)( |
||
foldTree(weightedCoverage)( |
foldTree(weightedCoverage)( |
||
# TREE FROM PARSE OF OUTLINE TEXT |
# --- TREE FROM PARSE OF OUTLINE TEXT ---- |
||
fmapTree( |
fmapTree( |
||
recordFromKeysDefaultsDelimiterAndLine |
recordFromKeysDefaultsDelimiterAndLine( |
||
columnTitles |
|||
)( |
|||
[str, float, float])([ |
|||
'?', 1.0, 0.0 |
|||
])(delimiter) |
|||
)( |
)( |
||
forestFromIndentLevels( |
|||
indentLevelsFromLines( |
|||
reportLines[1:] |
|||
) |
|||
)[0] |
|||
) |
) |
||
) |
) |
||
Line 2,419: | Line 2,425: | ||
# WEIGHTED COVERAGE, AND SHARE OF TOTAL RESIDUE |
# ---- WEIGHTED COVERAGE, AND SHARE OF TOTAL RESIDUE ----- |
||
# weightedCoverage :: Tree Dict -> |
# weightedCoverage :: Tree Dict -> |
||
Line 2,429: | Line 2,435: | ||
''' |
''' |
||
def go(xs): |
def go(xs): |
||
cws = [ |
|||
cws = [(r['coverage'], r['weight']) for r in [root(x) for x in xs]] |
|||
(r['coverage'], r['weight']) for r |
|||
in [root(x) for x in xs] |
|||
] |
|||
totalWeight = reduce(lambda a, x: a + x[1], cws, 0) |
totalWeight = reduce(lambda a, x: a + x[1], cws, 0) |
||
return Node(dict( |
return Node(dict( |
||
Line 2,439: | Line 2,448: | ||
} |
} |
||
))(xs) |
))(xs) |
||
return |
return go |
||
Line 2,448: | Line 2,457: | ||
''' |
''' |
||
def go(fraction, node): |
def go(fraction, node): |
||
[nodeRoot, nodeNest] = |
[nodeRoot, nodeNest] = ap([root, nest])([node]) |
||
weights = [root(x)['weight'] for x in nodeNest] |
weights = [root(x)['weight'] for x in nodeNest] |
||
siblingsTotal = sum(weights) |
siblingsTotal = sum(weights) |
||
Line 2,465: | Line 2,474: | ||
# |
# ------------------ OUTLINE TABULATION ------------------ |
||
# tabulation :: [Int] -> String -> Dict -> String |
# tabulation :: [Int] -> String -> Dict -> String |
||
Line 2,495: | Line 2,504: | ||
# |
# ------------ GENERIC AND REUSABLE FUNCTIONS ------------ |
||
# Node :: a -> [Tree a] -> Tree a |
# Node :: a -> [Tree a] -> Tree a |
||
Line 2,506: | Line 2,515: | ||
# |
# ap (<*>) :: [(a -> b)] -> [a] -> [b] |
||
def |
def ap(fs): |
||
'''Constructor for a pair of values, |
|||
possibly of two different types. |
|||
''' |
|||
return lambda y: ( |
|||
x + (y,) |
|||
) if isinstance(x, tuple) else (x, y) |
|||
# apList (<*>) :: [(a -> b)] -> [a] -> [b] |
|||
def apList(fs): |
|||
'''The application of each of a list of functions, |
'''The application of each of a list of functions, |
||
to each of a list of values. |
to each of a list of values. |
||
''' |
''' |
||
def go(xs): |
|||
return liftA2List(identity)(fs) |
|||
return [ |
|||
f(x) for (f, x) |
|||
in product(fs, xs) |
|||
] |
|||
return go |
|||
Line 2,548: | Line 2,552: | ||
# concatMap :: (a -> [b]) -> [a] -> [b] |
# concatMap :: (a -> [b]) -> [a] -> [b] |
||
def concatMap(f): |
def concatMap(f): |
||
'''A concatenated list |
'''A concatenated list over which a function has been mapped. |
||
The list monad can be derived by using a function f which |
|||
has been mapped. |
|||
wraps its output in a list, |
|||
(using an empty list to represent computational failure). |
|||
function which wraps its output in a list (using an |
|||
empty list to represent computational failure). |
|||
''' |
''' |
||
def go(xs): |
|||
return lambda xs: (''.join if isinstance(xs, str) else list)( |
|||
chain.from_iterable(map(f, xs)) |
return chain.from_iterable(map(f, xs)) |
||
return go |
|||
Line 2,565: | Line 2,568: | ||
# first :: (a -> b) -> ((a, c) -> (b, c)) |
|||
def firstArrow(f): |
|||
def first(f): |
|||
'''A simple function lifted to one which applies |
|||
'''A simple function lifted to a function over a tuple, |
|||
to a tuple, transforming only its first item. |
|||
with f applied only the first of two values. |
|||
''' |
''' |
||
return lambda xy: |
return lambda xy: (f(xy[0]), xy[1]) |
||
xy[1] |
|||
) |
|||
Line 2,593: | Line 2,595: | ||
def fmapTree(f): |
def fmapTree(f): |
||
'''A new tree holding the results of |
'''A new tree holding the results of |
||
an application of f to each root in |
|||
the existing tree. |
the existing tree. |
||
''' |
''' |
||
def go(x): |
def go(x): |
||
return Node |
return Node( |
||
f(x['root']) |
|||
) |
)([go(v) for v in x['nest']]) |
||
return |
return go |
||
Line 2,606: | Line 2,608: | ||
def foldTree(f): |
def foldTree(f): |
||
'''The catamorphism on trees. A summary |
'''The catamorphism on trees. A summary |
||
value |
value defined by a depth-first fold. |
||
''' |
''' |
||
def go(node): |
def go(node): |
||
return f( |
return f(root(node))([ |
||
go(x) for x in |
go(x) for x in nest(node) |
||
]) |
]) |
||
return |
return go |
||
# |
# forestFromIndentLevels :: [(Int, a)] -> [Tree a] |
||
def |
def forestFromIndentLevels(tuples): |
||
'''A list of trees derived from a list of |
'''A list of trees derived from a list of values paired |
||
with integers giving their levels of indentation. |
with integers giving their levels of indentation. |
||
''' |
''' |
||
def go(xs): |
def go(xs): |
||
if xs: |
if xs: |
||
intIndent, v = xs[0] |
|||
firstTreeLines, rest = span( |
|||
lambda x: intIndent < x[0] |
|||
)(xs[1:]) |
)(xs[1:]) |
||
return [Node( |
return [Node(v)(go(firstTreeLines))] + go(rest) |
||
else: |
else: |
||
return [] |
return [] |
||
Line 2,636: | Line 2,638: | ||
'''First member of a pair.''' |
'''First member of a pair.''' |
||
return tpl[0] |
return tpl[0] |
||
# identity :: a -> a |
|||
def identity(x): |
|||
'''The identity function.''' |
|||
return x |
|||
Line 2,651: | Line 2,647: | ||
''' |
''' |
||
indentTextPairs = list(map( |
indentTextPairs = list(map( |
||
compose( |
compose(first(len), span(isSpace)), |
||
xs |
xs |
||
)) |
)) |
||
Line 2,658: | Line 2,654: | ||
)(indentTextPairs)) |
)(indentTextPairs)) |
||
return list(map( |
return list(map( |
||
first(flip(div)(indentUnit)), |
|||
indentTextPairs |
indentTextPairs |
||
)) |
)) |
||
Line 2,670: | Line 2,666: | ||
''' |
''' |
||
def go(indent): |
def go(indent): |
||
return lambda node: [f(indent, node['root'])] + |
return lambda node: [f(indent, node['root'])] + list( |
||
concatMap( |
|||
go(strTab + indent) |
|||
)(node['nest']) |
|||
) |
|||
return lambda tree: '\n'.join(go('')(tree)) |
|||
Line 2,699: | Line 2,697: | ||
''' |
''' |
||
return s.isspace() |
return s.isspace() |
||
# liftA2List :: (a -> b -> c) -> [a] -> [b] -> [c] |
|||
def liftA2List(f): |
|||
'''The binary operator f lifted to a function over two |
|||
lists. f applied to each pair of arguments in the |
|||
cartesian product of xs and ys. |
|||
''' |
|||
return lambda xs: lambda ys: [ |
|||
f(x)(y) for x, y in product(xs, ys) |
|||
] |
|||
# lines :: String -> [String] |
|||
def lines(s): |
|||
'''A list of strings, |
|||
(containing no newline characters) |
|||
derived from a single new-line delimited string. |
|||
''' |
|||
return s.splitlines() |
|||
Line 2,770: | Line 2,748: | ||
span p xs is equivalent to (takeWhile p xs, dropWhile p xs). |
span p xs is equivalent to (takeWhile p xs, dropWhile p xs). |
||
''' |
''' |
||
def match(ab): |
|||
b = ab[1] |
|||
return not b or not p(b[0]) |
|||
def f(ab): |
|||
a, b = ab |
|||
return a + [b[0]], b[1:] |
|||
def go(xs): |
def go(xs): |
||
return until(match)(f)(([], xs)) |
|||
return go |
|||
until(lambda i: (lng == i) or not p(xs[i]))(succ)(0) |
|||
)(xs) |
|||
return lambda xs: go(xs) |
|||
# Unimplemented <- splitOn for lists (Eq a => [a] -> [a] -> [[a]]) |
|||
# splitOn :: String -> String -> [String] |
# splitOn :: String -> String -> [String] |
||
def splitOn(pat): |
def splitOn(pat): |
||
Line 2,786: | Line 2,768: | ||
return lambda xs: ( |
return lambda xs: ( |
||
xs.split(pat) if isinstance(xs, str) else None |
xs.split(pat) if isinstance(xs, str) else None |
||
) |
|||
# splitAt :: Int -> [a] -> ([a], [a]) |
|||
def splitAt(n): |
|||
'''A tuple pairing the prefix of length n |
|||
with the rest of xs. |
|||
''' |
|||
return lambda xs: (xs[0:n], xs[n:]) |
|||
# succ :: Enum a => a -> a |
|||
def succ(x): |
|||
'''The successor of a value. |
|||
For numeric types, (1 +). |
|||
''' |
|||
return 1 + x if isinstance(x, int) else ( |
|||
chr(1 + ord(x)) |
|||
) |
) |
||
Line 2,811: | Line 2,775: | ||
'''String in lower case.''' |
'''String in lower case.''' |
||
return s.lower() |
return s.lower() |
||
# 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) |
|||
Line 2,826: | Line 2,782: | ||
The initial seed value is x. |
The initial seed value is x. |
||
''' |
''' |
||
def go(f |
def go(f): |
||
def g(x): |
|||
v = x |
|||
while not p(v): |
|||
v = f(v) |
|||
return v |
|||
return g |
|||
return go |
|||