Skip to content

Instantly share code, notes, and snippets.

@UmbreLu
Last active November 5, 2024 17:01
Show Gist options
  • Save UmbreLu/b12c4fe79a54619804c928dc8224e014 to your computer and use it in GitHub Desktop.
Save UmbreLu/b12c4fe79a54619804c928dc8224e014 to your computer and use it in GitHub Desktop.
My answers in Python 3.8 for oskarkv/exercises.org on functional programing (https://gist.github.com/oskarkv/3168ea3f8d7530ccd94c97c19aafe266).
from functools import reduce
import operator
# The proposed exercises are expected to be solven with a
# pure functional aproach. This means using recursion, map,
# reduce, filter, clist and other functions.
# As my focus is learning the "Python Way", I'll also
# write some functions the most pythonic fashion I can.
# In currect state python, the proper pythonistic way
# would be to use recursion, generators, generator expressions
# and list comprehetions mostly.
# Defining local variables is tolerable if you can't substitute
# the reduce function with sum(), all(), any() or similar
# to update states (not great but readable at least).
# That's why those functions are now in functools or itertools
# and are not ready-to-use anymore.
x = [1, [[2], 3], [4], 5, [6, 100, [[7], [[8]], 9]], 10]
x = [1, [[2], 3], [4], 5, [6, 100, [[7], [[8]], 9]], 10]
# tree sum
def tree_sum(tree):
if not isinstance(tree, list):
return tree
else:
return reduce(operator.add, map(tree_sum, tree))
print(tree_sum(x))
#pythonic tree_sum
def tree_sum2(tree):
if not isinstance(tree, list):
return tree
else:
return sum(tree_sum2(x) for x in tree)
print(tree_sum2(x))
# calculate the depth of a tree (in the tree above, 8 is the deepest element)
def how_deep(tree):
if isinstance(tree, int):
return 0
else:
return max(map(how_deep, tree)) + 1
print(how_deep(x)) # => maximun depth is 5
# pythonic how_deep
def how_deep2(tree):
if isinstance(tree, int):
return 0
else:
return max(how_deep(x) for x in tree) + 1
print(how_deep2(x)) # => maximun depth is 5
# find the largest value in a tree
def largest_value(tree):
if isinstance(tree, int):
return tree
else:
return max(map(largest_value, tree))
print(largest_value(x)) # => 100
# pythonic largest_value
def largest_value2(tree):
if isinstance(tree, int):
return tree
else:
return max(largest_value2(x) for x in tree)
print(largest_value2(x)) # => 100
# Tower of Hanoi with prints inside
def hanoi(a, b, c, count):
def move(a, b):
print("Move disk from", a, "to", b)
if count == 1:
move(a, c)
else:
hanoi(a, c, b, count - 1)
move(a, c)
hanoi(b, a, c, count - 1)
print('Hanoi with print')
print(hanoi('A', 'B', 'C', 6))
# Tower of Hanoi returning string (totally pure)
def hanoi_string(a, b, c, count):
def move(a, b):
return f'Move disk from {a} to {b}.\n'
if count == 1:
return move(a, c)
else:
return (hanoi_string(a, c, b, count - 1) + move(a, c) + hanoi_string(b, a, c, count - 1))
print('Hanoi with string')
print(hanoi_string('A', 'B', 'C', 6))
def clist(*args): # or use list
return [*args]
print(clist(1, 2, 3))
def add_many(*args):
return reduce(operator.add, args)
print(add_many(1, 2, 3))
def sub_many(*args):
return reduce(operator.sub, args)
print(sub_many(1, 2, 3))
def negate(arg):
return -arg
def negate_many(*args):
return list(map(lambda x: -x, args))
print(negate_many(1, 2, 3))
def double(arg):
return arg * 2
def double_many(*args):
return reduce(double, args)
def compose(function1, function2):
def closure(*arg):
return function2(function1(*arg))
return closure
def compose_many(*funcs):
return reduce(compose, funcs)
# add_many and sub_many must come first because they take multiple args
# and reduce it to a single output, while negate and double take only
# one arg. Otherwise it would bug.
print(compose_many(add_many, negate, double)(1, 2, 3)) # => -12
print(compose_many(sub_many, double, clist)(1, 2, 3)) # => [-8]
# just change packing method to return list of lists
def zip_two(iter1, iter2):
def gen(x, y):
if isinstance(iter1[0], int):
for n in range(len(iter1)):
yield iter1[n], iter2[n]
else:
for n in range(len(iter1)):
yield iter1[n] + (iter2[n],)
return list(gen(iter1, iter2))
def myzip(*iterables):
return reduce(zip_two, iterables)
print(myzip([1, 2, 3], [4, 5, 6]))
print(myzip([1, 2, 3], [4, 5, 6], [7, 8, 9]))
print(list(zip([1, 2, 3], [4, 5, 6]))) # zip built-in function returns a zip object
print(zip([1, 2, 3], [4, 5, 6], [7, 8, 9])) # instead of just tuples
def zipmap(iter1, iter2):
def gen(x, y):
for n in range(len(iter1)):
yield iter1[n], iter2[n]
return dict(gen(iter1, iter2))
print(zipmap([1, 2, 3], [4, 5, 6])) # => {1: 4, 2: 5, 3: 6}
def zipwith(f, *iters):
def gen(x, *y):
for n in range(len(iters[0])):
yield reduce(x, (z[n] for z in y))
return list(gen(f, *iters))
print(zipwith(operator.add, [1, 2, 3], [4, 5, 6])) # => [5, 7, 9]
print(zipwith(operator.add, [1, 2, 3], [4, 5, 6], [1, 1, 1])) # => [6, 8, 10]
# car and cdr
# given cons:
def cons(a, b):
def pair(f):
return f(a, b)
return pair
# car(cons(3, 4)) returns 3, and cdr(cons(3, 4)) returns 4
def car(f):
return f(lambda x, y: x)
def cdr(f):
return f(lambda x, y: y)
car_imp = car(cons(1, 2))
print(car_imp)
cdr_imp = cdr(cons(1, 2))
print(cdr_imp)
def partial(f, *args):
def closure(*new_args):
return f(*args, *new_args)
return closure
print(partial(add_many, 1, 2)(3, 4)) # => 10
print(partial(clist, 1, 2)(3, 4)) # => [1, 2, 3, 4]
print(partial(sub_many, 10)(1, 2)) # => 7
def transpose(mat):
def inter(pos, mat):
for row in mat:
yield row[pos]
for n in range(len(mat[0])):
yield list(inter(n, mat))
print(list(transpose([[1, 2, 3], [4, 5, 6]]))) # => [[1, 4], [2, 5], [3, 6]]
def flip(f):
def closure(arg1, arg2, *args):
return f(arg2, arg1, *args)
return closure
print(flip(clist)(1, 2, 3)) # => [2, 1, 3]
print(flip(sub_many)(10, 1)) # => -9
def flips(f):
def closure(*args):
return f(*args[::-1])
return closure
print(flips(clist)(1, 2, 3)) # => [3, 2, 1]
print(flips(sub_many)(1, 2, 3)) # => 0
def take(n, seq):
return seq[:n]
print(take(3, list(range(10)))) # => [0, 1, 2]
def drop(n, seq):
return seq[n:]
print(drop(3, list(range(6)))) # => [3, 4, 5]
# functional pure flatten
def flatten(tree):
if isinstance(tree, int):
return clist(tree)
else:
return reduce(operator.add, map(flatten, tree))
print(flatten([1, [2, [3, 4], [5, 6], 7], 8, [9, 10]]))
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# pythonic flatten
def flatten2(tree):
if isinstance(tree, int):
return [tree]
else:
concatenated = []
for listed in (flatten2(x) for x in tree):
concatenated += listed
return concatenated
print(flatten2([1, [2, [3, 4], [5, 6], 7], 8, [9, 10]]))
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# generator
def interleave(*seqs):
for n in range(len(seqs[0])):
for s in range(len(seqs)):
yield seqs[s][n]
# genexp
def interleave2(*seqs):
return (seqs[s][n] for s in range(len(seqs)) for n in range(len(seqs[0])))
print(list(interleave([1, 2, 3], [10, 20, 30])))
# => [1, 10, 2, 20, 3, 30]
print(list(interleave([1, 2, 3], [10, 20, 30], "abc")))
# => [1, 10, "a", 2, 20, "b", 3, 30, "c"]
print(list(interleave2([1, 2, 3], [10, 20, 30])))
# => [1, 10, 2, 20, 3, 30]
print(list(interleave2([1, 2, 3], [10, 20, 30], "abc")))
# => [1, 10, "a", 2, 20, "b", 3, 30, "c"]
# every_pred pythonic functions (doesn't need to be high order function)
# and with multiple args
positive = lambda x: x > 0
even = lambda x: x % 2 == 0
def every_pred(*nums, **funcs):
return all(f(n) for f in funcs.values() for n in nums)
# every_pred(positive, even)(8) => True
print(every_pred(8, 10, 12, 14, f1=positive, f2=even))
# every_pred(positive, even)(7) => False
print(every_pred(7, positive, even))
# without defining functions
pos_even_nums = (2, 4, 6, 8)
every_pred_1 = all((x > 0 and x % 2 == 0 for x in pos_even_nums)) # => True
print(every_pred_1)
every_pred_2 = all(positive(x) and even(x) for x in pos_even_nums)
print(every_pred_2)
# single expression with single argument
every_pred_3 = 7 > 0 and 7 % 2 == 0
print(every_pred_3)
###### From this point the exercises are not updated
###### and are done in a beginner fashion
###### check only if you have no clue on how to solve
###### the problems
def frequencies(list_):
dict_ = {}
for x in list_:
if x not in dict_:
dict_[x] = 1
else:
dict_[x] += 1
return dict_
freq1 = frequencies("aabcbcac") #=> {'a': 3, 'c': 3, 'b': 2}
freq2 = frequencies([1, 2, 2, 2]) #=> {1: 1, 2: 3}
print(freq1)
print(freq2)
def partition(n, step, seq):
partit = []
for this_step in range(len(seq) - (len(seq) % 3)):
partit.append(seq[this_step:this_step + 3])
return partit
partit = partition(3, 1, [1, 2, 3, 4, 5]) #=> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
print(partit)
def merge_with(func, *dicts):
new_dict = dict(dicts[0])
for dict_ in dicts[1:]:
for key in dict_.keys():
if key in new_dict:
new_dict[key] = func(new_dict[key], dict_[key])
else:
new_dicts[key] = dict_[key]
return new_dict
def list_more(*args):
return list(args)
merge1 = merge_with(add, {"a": 1, "b": 2}, {"b": 2}) #=> {"a": 1, "b": 4}
merge2 = merge_with(list_more, {"a": 1, "b": 2}, {"b": 2}) #=> {"a": 1, "b": [2 2]}
print(merge1)
print(merge2)
def memoize(func):
def closure(*args):
if len(args) == 1:
true_arg = args[0]
else:
true_arg = args
memo = dict()
if not true_arg in memo:
memo[true_arg] = func(true_arg)
return memo[true_arg]
return closure
new_add2 = memoize(add)
n_add3 = new_add2(1, 2, 3) #=> 6
n_add4 = new_add2(1, 2, 3) #=> 6 (Did not actually compute add(1, 2, 3).)
print(n_add3)
print(n_add4)
def group_by(func, seq):
dict_ = dict()
for n in seq:
m = func(n)
if m in dict_:
dict_[m].append(n)
else:
dict_[m] = [n]
return dict_
print(group_by(len, ["hi", "dog", "me", "bad", "good"]))
#=> {2: ["hi", "me"], 3: ["dog", "bad"], 4: ["good"]}
def update(dict_, k, func, *args):
if len(args) == 1:
true_args = args[0]
else:
true_args = args
if k in dict_:
dict_[k] = func((dict_[k]) + (true_args))
else:
dict_[k] = func(true_args)
return dict_
bob = {"name": "bob", "hp": 3}
print(update(bob, "hp", myadd, 2))
#=> {"name": "bob", "hp": 5}
nohp = {"name": "bob"}
print(update(nohp, "hp", myadd, 2))
#=> {"name": "bob", "hp": 2}
def update_in(dict_, ks, func, *args):
if len(args) == 1:
true_args = args[0]
else:
true_args = args
if len(ks) > 1:
update_in(dict_[ks[0]], ks[1:], func, true_args)
else:
g = ks[0]
if g in dict_:
dict_[g] = func((dict_[g]) + (true_args))
else:
dict_[g] = func(true_args)
return dict_
a = {"a": 1, "b": {"c": 2, "d": {"e": 3}}}
print(update_in(a, ["b", "d", "e"], myadd, 10))
#=> {"a": 1, "b": {"c": 2, "d": {"e": 13}}}
print(update_in(a, ["b", "d", "f"], myadd, 10))
#=> {"a": 1, "b": {"c": 2, "d": {"e": 3, "f": 10}}}
def balanced(string): #returns true/false for balanced marks in string
match = {')': '(', ']': '[', '}': '{'}
last_open = '@'
print(string)
for pos, char in enumerate(string):
if char in '([{':
last_open = char
last_pos = pos
if char in ')]}':
if last_open != match[char]:
return False
else:
if balanced(string[:last_pos] + string[pos + 1:]):
return True
else:
return False
if last_open == '@':
return True
else:
return False
balanced0 = balanced('aaaaaaa(a)aaaaaa')
balanced1 = balanced("abc(def{g}hi[jk]((()))l)m") #=> True
balanced2 = balanced("a(b") #=> False
balanced3 = balanced("([)]") #=> False
print(balanced0)
print(balanced1)
print(balanced2)
print(balanced3)
def wrap_unless_zero(n):
if isinstance(n, int) and n > 0:
return [n - 1]
else:
return n
def postwalk(f, tree):
for p, n in enumerate(tree):
if not isinstance(n, list):
tree[p] = f(n)
else:
postwalk(f, n)
return tree
def prewalk(f, tree):
for p, n in enumerate(tree):
if not isinstance(n, list):
tree[p] = f(n)
if isinstance(tree[p], list):
prewalk(f, tree[p])
else:
prewalk(f, n)
return tree
print('walkers')
tree = [1, [2, [3]]]
postwalk(wrap_unless_zero, tree) #=> [[0] [[1] [[2]]]]
print(tree)
print(prewalk(wrap_unless_zero, [1, [2, [3]]])) #=> [[0] [[[0]] [[[[0]]]]]]
def mymap(f, list_):
temp = []
for n in list_:
temp.append(f(n))
return temp
print(mymap(lambda x: x+3, [1, 2, 3, 4, 50, 60, 70, 80]))
def myfilter(f, list_):
temp = []
for n in list_:
if f(n):
temp.append(n)
return temp
print(myfilter(lambda x: x%2 == 0, range(10)))
def myreduce(f, list_):
current = list_[0]
for n in list_[1:]:
current = f(current, n)
return current
print(myreduce(lambda x, y: x + y, range(10)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment