# Functional style linked-lists. (2.01)
from functools import reduce
class Pair:
def __init__(self, first=None, rest=None):
self.first = first
self.rest = rest
def __repr__(self):
return '(' + ' '.join(map(str, ltoa(self))) + ')'
def _append(tail, l):
assert l is None or isinstance(l, Pair), 'Pair expected!'
# @internal
# Copy and append elements.
while l:
tail.rest = Pair(l.first)
tail = tail.rest
l = l.rest
return tail
def _reverse(l):
# @internal
# Reverse a list (destructive).
prev = None
while l:
rest = l.rest
l.rest = prev
prev = l
l = rest
return prev
def atol(a):
# Iterable to linked-list.
return _reverse(reduce(lambda init, x: Pair(x, init), a, None))
def ltoa(l):
# Linked-list to array.
return foldl(lambda init, x: init + [x], [], l)
def appendl(ls):
# Append all lists.
temp = Pair()
foldl(lambda init, x: _append(init, x), temp, ls)
return temp.rest
def reversel(l):
# Reverse a list.
return foldl(lambda init, x: Pair(x, init), None, l)
def foldl(proc, init, l):
# Build result by applying a procedure to list elements.
return foldlp(lambda init, p: proc(init, p.first), init, l)
def foldlp(proc, init, l):
# Build result by applying a procedure to list pairs.
while l:
init = proc(init, l)
l = l.rest
return init
def foldrightl(proc, init, l):
# Build result by applying a procedure to list elements last to first.
return foldrightlp(lambda init, p: proc(init, p.first), init, l)
def foldrightlp(proc, init, l):
# Build result by applying a procedure to list pairs last to first.
# (Prototypical implementation; limited by maximum recursion depth)
if not l:
return init
return proc(foldrightlp(proc, init, l.rest), l)
def foldrightlp(proc, init, l):
# Build result by applying a procedure to list pairs last to first.
# (Continuation passing style and trampoline eliminates recursion;
# precludes callable result)
def cps_detail(l, k):
if not l:
return k(init)
return lambda: cps_detail(l.rest, lambda x: lambda: k(proc(x, l)))
def trampoline(t):
while callable(t):
t = t()
return t
return trampoline(cps_detail(l, lambda x: x))
def foldrightlp(proc, init, l):
# Build result by applying a procedure to list pairs last to first.
# (Fold over reversed list of pairs; requires temporary list)
return foldl(proc, init, foldlp(lambda init, p: Pair(p, init), None, l))
def mapl(proc, l):
# Map procedure over list elements; return list of results.
return maplp(lambda p: proc(p.first), l)
def maplp(proc, l):
# Map procedure over list pairs; return list of results.
# (Fold is more efficient than foldright but ...)
return _reverse(foldlp(lambda init, p: Pair(proc(p), init), None, l))
def maplp(proc, l):
# Map procedure over list pairs; return list of results.
# (... I want to test foldright)
return foldrightlp(lambda init, p: Pair(proc(p), init), None, l)
def appendmapl(proc, l):
# Map procedure over list elements; return appended results.
return appendmaplp(lambda p: proc(p.first), l)
def appendmaplp(proc, l):
# Map procedure over list pairs; return appended results.
return appendl(maplp(proc, l))
def combinationsl(l, k):
# Generate ordered k-length combinations from list.
if k == 0:
return Pair()
return appendmaplp(lambda r: mapl(lambda c: Pair(r.first, c),
combinationsl(r.rest, k-1)), l)
# Test.
from itertools import combinations
n = 5
l = atol(range(n))
l2 = atol(range(n, n*2))
print(l)
print(ltoa(l))
print(reversel(l))
print(mapl(lambda x: x**2, l))
print(appendl(atol([None, l, None, None, l2, None])))
print(mapl(lambda x: Pair(x), l))
print(appendmapl(lambda x: Pair(x), l))
def reformat(ls):
return (tuple(ltoa(x)) for x in ltoa(ls))
n = 4
a = range(1, 1+n)
l = atol(a)
for k in range(1+n):
x = list(reformat(combinationsl(l, k)))
y = list(combinations(a, k))
print(x)
print(y)
assert x == y, f'Mismatch for {n},{k}'
print('Okay.')
# Time.
from time import process_time
def _timeit(thunk):
try:
t = process_time()
thunk()
print('Elapsed: {:.6f}s'.format(process_time() - t))
except Exception as e:
print('Invalid:', e)
def make_f(n, k, count):
l = atol(range(n))
def f():
for _ in range(count):
combinationsl(l, k)
return f
_timeit(make_f(8, 4, 475))
_timeit(make_f(16, 8, 1))
IyBGdW5jdGlvbmFsIHN0eWxlIGxpbmtlZC1saXN0cy4gKDIuMDEpCgpmcm9tIGZ1bmN0b29scyBpbXBvcnQgcmVkdWNlCgpjbGFzcyBQYWlyOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGZpcnN0PU5vbmUsIHJlc3Q9Tm9uZSk6CiAgICAgICAgc2VsZi5maXJzdCA9IGZpcnN0CiAgICAgICAgc2VsZi5yZXN0ID0gcmVzdAoKICAgIGRlZiBfX3JlcHJfXyhzZWxmKToKICAgICAgICByZXR1cm4gJygnICsgJyAnLmpvaW4obWFwKHN0ciwgbHRvYShzZWxmKSkpICsgJyknCgpkZWYgX2FwcGVuZCh0YWlsLCBsKToKICAgIGFzc2VydCBsIGlzIE5vbmUgb3IgaXNpbnN0YW5jZShsLCBQYWlyKSwgJ1BhaXIgZXhwZWN0ZWQhJwogICAgIyBAaW50ZXJuYWwKICAgICMgQ29weSBhbmQgYXBwZW5kIGVsZW1lbnRzLgogICAgd2hpbGUgbDoKICAgICAgICB0YWlsLnJlc3QgPSBQYWlyKGwuZmlyc3QpCiAgICAgICAgdGFpbCA9IHRhaWwucmVzdAogICAgICAgIGwgPSBsLnJlc3QKICAgIHJldHVybiB0YWlsCgpkZWYgX3JldmVyc2UobCk6CiAgICAjIEBpbnRlcm5hbAogICAgIyBSZXZlcnNlIGEgbGlzdCAoZGVzdHJ1Y3RpdmUpLgogICAgcHJldiA9IE5vbmUKICAgIHdoaWxlIGw6CiAgICAgICAgcmVzdCA9IGwucmVzdAogICAgICAgIGwucmVzdCA9IHByZXYKICAgICAgICBwcmV2ID0gbAogICAgICAgIGwgPSByZXN0CiAgICByZXR1cm4gcHJldgoKZGVmIGF0b2woYSk6CiAgICAjIEl0ZXJhYmxlIHRvIGxpbmtlZC1saXN0LgogICAgcmV0dXJuIF9yZXZlcnNlKHJlZHVjZShsYW1iZGEgaW5pdCwgeDogUGFpcih4LCBpbml0KSwgYSwgTm9uZSkpCgpkZWYgbHRvYShsKToKICAgICMgTGlua2VkLWxpc3QgdG8gYXJyYXkuCiAgICByZXR1cm4gZm9sZGwobGFtYmRhIGluaXQsIHg6IGluaXQgKyBbeF0sIFtdLCBsKQoKZGVmIGFwcGVuZGwobHMpOgogICAgIyBBcHBlbmQgYWxsIGxpc3RzLgogICAgdGVtcCA9IFBhaXIoKQogICAgZm9sZGwobGFtYmRhIGluaXQsIHg6IF9hcHBlbmQoaW5pdCwgeCksIHRlbXAsIGxzKQogICAgcmV0dXJuIHRlbXAucmVzdAoKZGVmIHJldmVyc2VsKGwpOgogICAgIyBSZXZlcnNlIGEgbGlzdC4KICAgIHJldHVybiBmb2xkbChsYW1iZGEgaW5pdCwgeDogUGFpcih4LCBpbml0KSwgTm9uZSwgbCkKCmRlZiBmb2xkbChwcm9jLCBpbml0LCBsKToKICAgICMgQnVpbGQgcmVzdWx0IGJ5IGFwcGx5aW5nIGEgcHJvY2VkdXJlIHRvIGxpc3QgZWxlbWVudHMuCiAgICByZXR1cm4gZm9sZGxwKGxhbWJkYSBpbml0LCBwOiBwcm9jKGluaXQsIHAuZmlyc3QpLCBpbml0LCBsKQoKZGVmIGZvbGRscChwcm9jLCBpbml0LCBsKToKICAgICMgQnVpbGQgcmVzdWx0IGJ5IGFwcGx5aW5nIGEgcHJvY2VkdXJlIHRvIGxpc3QgcGFpcnMuCiAgICB3aGlsZSBsOgogICAgICAgIGluaXQgPSBwcm9jKGluaXQsIGwpCiAgICAgICAgbCA9IGwucmVzdAogICAgcmV0dXJuIGluaXQKCmRlZiBmb2xkcmlnaHRsKHByb2MsIGluaXQsIGwpOgogICAgIyBCdWlsZCByZXN1bHQgYnkgYXBwbHlpbmcgYSBwcm9jZWR1cmUgdG8gbGlzdCBlbGVtZW50cyBsYXN0IHRvIGZpcnN0LgogICAgcmV0dXJuIGZvbGRyaWdodGxwKGxhbWJkYSBpbml0LCBwOiBwcm9jKGluaXQsIHAuZmlyc3QpLCBpbml0LCBsKQoKZGVmIGZvbGRyaWdodGxwKHByb2MsIGluaXQsIGwpOgogICAgIyBCdWlsZCByZXN1bHQgYnkgYXBwbHlpbmcgYSBwcm9jZWR1cmUgdG8gbGlzdCBwYWlycyBsYXN0IHRvIGZpcnN0LgogICAgIyAoUHJvdG90eXBpY2FsIGltcGxlbWVudGF0aW9uOyBsaW1pdGVkIGJ5IG1heGltdW0gcmVjdXJzaW9uIGRlcHRoKQogICAgaWYgbm90IGw6CiAgICAgICAgcmV0dXJuIGluaXQKICAgIHJldHVybiBwcm9jKGZvbGRyaWdodGxwKHByb2MsIGluaXQsIGwucmVzdCksIGwpCgpkZWYgZm9sZHJpZ2h0bHAocHJvYywgaW5pdCwgbCk6CiAgICAjIEJ1aWxkIHJlc3VsdCBieSBhcHBseWluZyBhIHByb2NlZHVyZSB0byBsaXN0IHBhaXJzIGxhc3QgdG8gZmlyc3QuCiAgICAjIChDb250aW51YXRpb24gcGFzc2luZyBzdHlsZSBhbmQgdHJhbXBvbGluZSBlbGltaW5hdGVzIHJlY3Vyc2lvbjsKICAgICMgcHJlY2x1ZGVzIGNhbGxhYmxlIHJlc3VsdCkKICAgIGRlZiBjcHNfZGV0YWlsKGwsIGspOgogICAgICAgIGlmIG5vdCBsOgogICAgICAgICAgICByZXR1cm4gayhpbml0KQogICAgICAgIHJldHVybiBsYW1iZGE6IGNwc19kZXRhaWwobC5yZXN0LCBsYW1iZGEgeDogbGFtYmRhOiBrKHByb2MoeCwgbCkpKQogICAgZGVmIHRyYW1wb2xpbmUodCk6CiAgICAgICAgd2hpbGUgY2FsbGFibGUodCk6CiAgICAgICAgICAgIHQgPSB0KCkKICAgICAgICByZXR1cm4gdAogICAgcmV0dXJuIHRyYW1wb2xpbmUoY3BzX2RldGFpbChsLCBsYW1iZGEgeDogeCkpCgpkZWYgZm9sZHJpZ2h0bHAocHJvYywgaW5pdCwgbCk6CiAgICAjIEJ1aWxkIHJlc3VsdCBieSBhcHBseWluZyBhIHByb2NlZHVyZSB0byBsaXN0IHBhaXJzIGxhc3QgdG8gZmlyc3QuCiAgICAjIChGb2xkIG92ZXIgcmV2ZXJzZWQgbGlzdCBvZiBwYWlyczsgcmVxdWlyZXMgdGVtcG9yYXJ5IGxpc3QpCiAgICByZXR1cm4gZm9sZGwocHJvYywgaW5pdCwgZm9sZGxwKGxhbWJkYSBpbml0LCBwOiBQYWlyKHAsIGluaXQpLCBOb25lLCBsKSkKCmRlZiBtYXBsKHByb2MsIGwpOgogICAgIyBNYXAgcHJvY2VkdXJlIG92ZXIgbGlzdCBlbGVtZW50czsgcmV0dXJuIGxpc3Qgb2YgcmVzdWx0cy4KICAgIHJldHVybiBtYXBscChsYW1iZGEgcDogcHJvYyhwLmZpcnN0KSwgbCkKCmRlZiBtYXBscChwcm9jLCBsKToKICAgICMgTWFwIHByb2NlZHVyZSBvdmVyIGxpc3QgcGFpcnM7IHJldHVybiBsaXN0IG9mIHJlc3VsdHMuCiAgICAjIChGb2xkIGlzIG1vcmUgZWZmaWNpZW50IHRoYW4gZm9sZHJpZ2h0IGJ1dCAuLi4pCiAgICByZXR1cm4gX3JldmVyc2UoZm9sZGxwKGxhbWJkYSBpbml0LCBwOiBQYWlyKHByb2MocCksIGluaXQpLCBOb25lLCBsKSkKCmRlZiBtYXBscChwcm9jLCBsKToKICAgICMgTWFwIHByb2NlZHVyZSBvdmVyIGxpc3QgcGFpcnM7IHJldHVybiBsaXN0IG9mIHJlc3VsdHMuCiAgICAjICguLi4gSSB3YW50IHRvIHRlc3QgZm9sZHJpZ2h0KQogICAgcmV0dXJuIGZvbGRyaWdodGxwKGxhbWJkYSBpbml0LCBwOiBQYWlyKHByb2MocCksIGluaXQpLCBOb25lLCBsKQoKZGVmIGFwcGVuZG1hcGwocHJvYywgbCk6CiAgICAjIE1hcCBwcm9jZWR1cmUgb3ZlciBsaXN0IGVsZW1lbnRzOyByZXR1cm4gYXBwZW5kZWQgcmVzdWx0cy4KICAgIHJldHVybiBhcHBlbmRtYXBscChsYW1iZGEgcDogcHJvYyhwLmZpcnN0KSwgbCkKCmRlZiBhcHBlbmRtYXBscChwcm9jLCBsKToKICAgICMgTWFwIHByb2NlZHVyZSBvdmVyIGxpc3QgcGFpcnM7IHJldHVybiBhcHBlbmRlZCByZXN1bHRzLgogICAgcmV0dXJuIGFwcGVuZGwobWFwbHAocHJvYywgbCkpCgpkZWYgY29tYmluYXRpb25zbChsLCBrKToKICAgICMgR2VuZXJhdGUgb3JkZXJlZCBrLWxlbmd0aCBjb21iaW5hdGlvbnMgZnJvbSBsaXN0LgogICAgaWYgayA9PSAwOgogICAgICAgIHJldHVybiBQYWlyKCkKICAgIHJldHVybiBhcHBlbmRtYXBscChsYW1iZGEgcjogbWFwbChsYW1iZGEgYzogUGFpcihyLmZpcnN0LCBjKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21iaW5hdGlvbnNsKHIucmVzdCwgay0xKSksIGwpCgojIFRlc3QuCgpmcm9tIGl0ZXJ0b29scyBpbXBvcnQgY29tYmluYXRpb25zCgpuID0gNQpsID0gYXRvbChyYW5nZShuKSkKbDIgPSBhdG9sKHJhbmdlKG4sIG4qMikpCnByaW50KGwpCnByaW50KGx0b2EobCkpCnByaW50KHJldmVyc2VsKGwpKQpwcmludChtYXBsKGxhbWJkYSB4OiB4KioyLCBsKSkKcHJpbnQoYXBwZW5kbChhdG9sKFtOb25lLCBsLCBOb25lLCBOb25lLCBsMiwgTm9uZV0pKSkKcHJpbnQobWFwbChsYW1iZGEgeDogUGFpcih4KSwgbCkpCnByaW50KGFwcGVuZG1hcGwobGFtYmRhIHg6IFBhaXIoeCksIGwpKQoKZGVmIHJlZm9ybWF0KGxzKToKICAgIHJldHVybiAodHVwbGUobHRvYSh4KSkgZm9yIHggaW4gbHRvYShscykpCgpuID0gNAphID0gcmFuZ2UoMSwgMStuKQpsID0gYXRvbChhKQpmb3IgayBpbiByYW5nZSgxK24pOgogICAgeCA9IGxpc3QocmVmb3JtYXQoY29tYmluYXRpb25zbChsLCBrKSkpCiAgICB5ID0gbGlzdChjb21iaW5hdGlvbnMoYSwgaykpCiAgICBwcmludCh4KQogICAgcHJpbnQoeSkKICAgIGFzc2VydCB4ID09IHksIGYnTWlzbWF0Y2ggZm9yIHtufSx7a30nCnByaW50KCdPa2F5LicpCgojIFRpbWUuCgpmcm9tIHRpbWUgaW1wb3J0IHByb2Nlc3NfdGltZQoKZGVmIF90aW1laXQodGh1bmspOgogICAgdHJ5OgogICAgICAgIHQgPSBwcm9jZXNzX3RpbWUoKQogICAgICAgIHRodW5rKCkKICAgICAgICBwcmludCgnRWxhcHNlZDogezouNmZ9cycuZm9ybWF0KHByb2Nlc3NfdGltZSgpIC0gdCkpCiAgICBleGNlcHQgRXhjZXB0aW9uIGFzIGU6CiAgICAgICAgcHJpbnQoJ0ludmFsaWQ6JywgZSkKCmRlZiBtYWtlX2YobiwgaywgY291bnQpOgogICAgbCA9IGF0b2wocmFuZ2UobikpCiAgICBkZWYgZigpOgogICAgICAgIGZvciBfIGluIHJhbmdlKGNvdW50KToKICAgICAgICAgICAgY29tYmluYXRpb25zbChsLCBrKQogICAgcmV0dXJuIGYKCl90aW1laXQobWFrZV9mKDgsIDQsIDQ3NSkpCl90aW1laXQobWFrZV9mKDE2LCA4LCAxKSk=
(0 1 2 3 4)
[0, 1, 2, 3, 4]
(4 3 2 1 0)
(0 1 4 9 16)
(0 1 2 3 4 5 6 7 8 9)
((0) (1) (2) (3) (4))
(0 1 2 3 4)
[()]
[()]
[(1,), (2,), (3,), (4,)]
[(1,), (2,), (3,), (4,)]
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
[(1, 2, 3, 4)]
[(1, 2, 3, 4)]
Okay.
Elapsed: 0.572445s
Elapsed: 0.426379s