RosettaCodeData/Task/Multisplit/Python/multisplit-4.py

77 lines
1.8 KiB
Python

'''Multisplit'''
from functools import reduce
# multiSplit :: [String] -> String -> [(String, String, Int)]
def multiSplit(separators):
'''List of triples:
[(token, separator, start index of separator].
'''
def go(s):
def f(tokensPartsOffset, ic):
tokens, parts, offset = tokensPartsOffset
i, c = ic
inDelim = offset > i
return maybe(
(
tokens if inDelim
else c + tokens, parts, offset
)
)(
lambda x: (
'',
[(tokens, x, i)] + parts,
i + len(x)
)
)(
None if inDelim else find(
s[i:].startswith
)(separators)
)
ts, ps, _ = reduce(f, enumerate(s), ('', [], 0))
return list(reversed(ps)) + [(ts, '', len(s))]
return go
# ------------------------- TEST -------------------------
# main :: IO ()
def main():
'''String split on three successive separators.'''
print(
multiSplit(['==', '!=', '='])(
'a!===b=!=c'
)
)
# ------------------ GENERIC FUNCTIONS -------------------
# find :: (a -> Bool) -> [a] -> (a | None)
def find(p):
'''Just the first element in the list that matches p,
or None if no elements match.
'''
def go(xs):
try:
return next(x for x in xs if p(x))
except StopIteration:
return None
return go
# maybe :: b -> (a -> b) -> (a | None) -> b
def maybe(v):
'''Either the default value v, if m is None,
or the application of f to x.
'''
return lambda f: lambda m: v if (
None is m
) else f(m)
# MAIN ---
if __name__ == '__main__':
main()