misc/python/prelude.py

202 lines
5.8 KiB
Python
Raw Normal View History

2018-08-05 18:53:07 +02:00
## Haskell prelude in python
from functools import reduce, partial
from ast import literal_eval
from math import *
# make a function an infix operator
class infix:
def __init__(self, f):
self.f = f
def __or__(self, other):
return self.f(other)
def __ror__(self, other):
return infix(partial(self.f, other))
def __call__(self, x, y):
return self.f(x, y)
# make function composable
class compose:
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
def __mul__(self, other):
return compose(self._compose(other))
def __lshift__(self, x):
return self.f(x)
def _compose(self, g):
return lambda *args, **kwargs: self.f(g(*args, **kwargs))
# curry and compose a function
def curry(x, argc=None):
if argc is None:
try:
argc = x.__code__.co_argcount
except AttributeError:
# if x is a composable
argc = x.f.__code__.co_argcount
def p(*a):
if len(a) == argc:
return x(*a)
def q(*b):
return x(*(a + b))
return curry(q, argc - len(a))
return compose(p)
# functions to be redefined
_print, _range = print, range
_filter, _map, _zip = filter, map, zip
_list, _str, _chr = list, str, chr
_all, _any = all, any
# python
list = compose (_list)
chr = compose (_chr)
range = compose (lambda a, b, s=1:
list << _range(a, b+1, s) if isinstance (a, int)
else map (chr) << _range (ord(a), ord(b)+1, s))
# Operators
add = curry (lambda y, x: x + y)
sub = curry (lambda y, x: x - y)
mult = curry (lambda y, x: x * y)
quot = infix (lambda x, y: x // y)
rem = infix (lambda x, y: x % y)
# Tuples
fst = compose (lambda t: t[0])
snd = compose (lambda t: t[1])
# Higher order functions
map = curry (lambda f, xs: list << _map (f, xs))
filter = curry (lambda f, xs: list << _filter (f, xs))
foldl = curry (lambda f, xs: reduce (f, xs))
foldr = curry (lambda f, xs: reduce (f, xs[::-1]))
# Miscellaneous
flip = curry (lambda f, a, b: f (b, a))
id = compose (lambda x: x)
const = compose (lambda x, _: x)
do = lambda *x: any([x])
# Boolean
no = compose (lambda x: not x)
ee = curry (lambda x, y : x and y)
oo = curry (lambda x, y : x or y)
all = curry (lambda xs: _all(xs))
any = curry (lambda xs: _any(xs))
# Numerical
even = compose (lambda x: x % 2 == 0)
odd = no * even
gcd = compose (lambda x, y,
gcd_=lambda x, y: x if y == 0
else gcd (y, x |rem| y):
gcd_ (abs(x), abs(y)))
lcm = compose (lambda x, y: 0 if no (x or y)
else (x |quot| gcd (x, y))*2)
# List
head = compose (lambda xs: xs[0])
last = compose (lambda xs: xs[-1])
tail = compose (lambda xs: xs[1:])
init = compose (lambda xs: xs[0:-1])
null = compose (lambda xs: len (xs) == 0)
length = compose (len)
reverse = compose (lambda xs: xs[::-1])
# Special folds
sum = compose (lambda xs: foldl (add, xs))
product = compose (lambda xs: foldl (mult, xs))
concat = compose (lambda xs:
(lambda r=[x for x in (x for x in xs)]:
''.join(r) if isinstance(xs[0], str)
else r)())
concatMap = curry (lambda f, xs: concat << map (f) (xs))
maximum = compose (lambda xs: max (xs))
minimum = compose (lambda xs: min (xs))
# Building lists
scanl = curry (lambda f, z: add ([z]) * map (f (z)))
scanr = curry (lambda f, z: scanl (f, z) * reverse)
# Sublist
take = curry (lambda n, xs: xs[:n])
drop = curry (lambda n, xs: xs[n:])
splitAt = curry (lambda n, xs: (xs[:n], xs[n:]))
takeWhile = curry (lambda p, xs:
[] if no * p << head (xs)
else [head (xs)] + takeWhile (p) (tail (xs)))
dropWhile = curry (lambda p, xs:
[] if null (xs)
else dropWhile (p) << tail (xs) if p << head (xs)
else xs)
span = curry (lambda p, xs:
(xs, xs) if null (xs)
else ([], xs) if no * p << head (xs)
else (lambda k = span (p, tail (xs)):
([head (xs)] ++ fst (k), snd (k))
)())
break_ = curry (lambda p, xs: span (no * pi) (xs))
# Searching lists
elem = curry (lambda x, xs: x in xs)
notElem = curry (no * elem)
lookup = curry (lambda x, ks: ks.get(x))
# Zipping and unzipping lists
zip = curry (lambda xs, ys: list << _zip (xs, ys))
zip3 = curry (lambda xs, ys, zs: list << _zip (xs, ys, zs))
zipWith = curry (lambda f, xs, ys: list << _map (f, xs, ys))
zipWith3 = curry (lambda f, xs, ys, zs: list << _map (f, xs, ys, zs))
unzip = compose (lambda xs: list << _zip(*xs))
unzip3 = unzip
# Functions on strings
lines = compose (lambda x: x.split('\n'))
words = compose (lambda x: x.split())
unlines = compose (lambda x: '\n'.join(x))
unwords = compose (lambda x: ' '.join(x))
# Converting to String
show = str
# Converting from String
read = compose (literal_eval)
# Output functions
putStr = compose (lambda x: _print (x, end=""))
putStrLn = compose (lambda x: _print (x))
print = putStrLn * show
# Input functions
getLine = compose (input)
main = do (
print ("Is this Haskell?"),
# Is this a let?
(lambda n = mult (3) * product * filter (odd) * init << range (1, 10),
k = 14 |quot| 3, # infix?
e = lcm (620, 12):
print << (n, k, e)
)(),
print * concat << range ('a', 'f'),
print * scanl (mult, 2) * snd * splitAt (3) << range (10, 20),
print * concatMap (add ("! ")) << ["hey", "hi", "what"],
print << dropWhile (odd) ([1,3,5,8,11,12]),
print << zip3 ([1,2,3], [4,5,6], [3,4,5]),
)