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 = lines(REPORT)
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(columnTitles)(
recordFromKeysDefaultsDelimiterAndLine(
[str, float, float])(['?', 1.0, 0.0])(delimiter)
columnTitles
)(
[str, float, float])([
'?', 1.0, 0.0
])(delimiter)
)(
)(
forestFromLineIndents(indentLevelsFromLines(
forestFromIndentLevels(
reportLines[1:]
indentLevelsFromLines(
))[0]
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 lambda xs: go(xs)
return go




Line 2,448: Line 2,457:
'''
'''
def go(fraction, node):
def go(fraction, node):
[nodeRoot, nodeNest] = apList([root, nest])([node])
[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 --------------------------------------
# ------------------ OUTLINE TABULATION ------------------


# tabulation :: [Int] -> String -> Dict -> String
# tabulation :: [Int] -> String -> Dict -> String
Line 2,495: Line 2,504:




# GENERIC AND REUSABLE FUNCTIONS --------------------------
# ------------ GENERIC AND REUSABLE FUNCTIONS ------------


# Node :: a -> [Tree a] -> Tree a
# Node :: a -> [Tree a] -> Tree a
Line 2,506: Line 2,515:




# Tuple (,) :: a -> b -> (a, b)
# ap (<*>) :: [(a -> b)] -> [a] -> [b]
def Tuple(x):
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 or string over which a function f
'''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.
The list monad can be derived by using an (a -> [b])
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: Tuple(f(xy[0]))(
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
applying f to each root in
an application of f to each root in
the existing tree.
the existing tree.
'''
'''
def go(x):
def go(x):
return Node(f(x['root']))(
return Node(
[go(v) for v in x['nest']]
f(x['root'])
)
)([go(v) for v in x['nest']])
return lambda tree: go(tree)
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 obtained by a depth-first fold.
value defined by a depth-first fold.
'''
'''
def go(node):
def go(node):
return f(node['root'])([
return f(root(node))([
go(x) for x in node['nest']
go(x) for x in nest(node)
])
])
return lambda tree: go(tree)
return go




# forestFromLineIndents :: [(Int, String)] -> [Tree String]
# forestFromIndentLevels :: [(Int, a)] -> [Tree a]
def forestFromLineIndents(tuples):
def forestFromIndentLevels(tuples):
'''A list of trees derived from a list of lines paired
'''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, txt) = xs[0]
intIndent, v = xs[0]
(firstTreeLines, rest) = span(
firstTreeLines, rest = span(
compose(lt(intIndent), fst)
lambda x: intIndent < x[0]
)(xs[1:])
)(xs[1:])
return [Node(txt)(go(firstTreeLines))] + go(rest)
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(firstArrow(len), span(isSpace)),
compose(first(len), span(isSpace)),
xs
xs
))
))
Line 2,658: Line 2,654:
)(indentTextPairs))
)(indentTextPairs))
return list(map(
return list(map(
firstArrow(flip(div)(indentUnit)),
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'])] + concatMap(
return lambda node: [f(indent, node['root'])] + list(
go(strTab + indent)
concatMap(
)(node['nest'])
go(strTab + indent)
return lambda tree: unlines(go('')(tree))
)(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):
lng = len(xs)
return until(match)(f)(([], xs))
return splitAt(
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, x):
def go(f):
v = x
def g(x):
while not p(v):
v = x
v = f(v)
while not p(v):
return v
v = f(v)
return lambda f: lambda x: go(f, x)
return v
return g
return go