Enteleform
4/6/2017 - 12:31 PM

"So You Want to be a Functional Programmer" examples, transcribed to Python. Info & additional resources @ https://www.reddit.com/r/learnpyt

"So You Want to be a Functional Programmer" examples, transcribed to Python. Info & additional resources @ https://www.reddit.com/r/learnpython/comments/63su2u/so_you_want_to_be_a_functional_programmer/




#####  Higher-Order Functions (1)  #####
  123 Main St.:  Valid Address
  Main St.:  Invalid Address
  Joe Mama:  Valid Name
  Joe 'l33tz0rs' Mama:  Invalid Name



#####  Higher-Order Functions (2)  #####
  10
  20
  30
  40



#####  Clojures  #####
  [['1-1', '1-2'], ['2-1', '2-2'], ['3-1', '3-2']]
  [['1-1', '1-2'], ['###', '2-2'], ['$$$', '3-2']]
  [['1-1', '1-2'], ['2-1', '2-2'], ['@@@', '3-2']]
  [['100', '1-2'], ['200', '2-2'], ['300', '3-2']]



#####  Point-Free Notation, Function Composition, Currying  #####
  140
  140
  300
  300



#####  Partial Application  #####
  {Joe}
  {{Joe}}



#####  Map  #####
  [10, 20, 30, 40, 50]
  [20, 40, 60, 80, 100]
  [1, 2, 3, 4, 5]



#####  Filter  #####
  [1, 3, 5]
  [2, 4]
  [1, 2, 3, 4, 5]



#####  Reduce  #####
  6
  15
  [1, 2, 3, 4, 5]


from functools    import reduce, partial as F
from syntax_sugar import pipe
import re


def test(name, *results):
	result_lines = "\n".join(["  " + str(x) for x in results])
	print("\n\n\n#####  " + name + "  #####\n" + result_lines)


def square(x):     return(x*x)
def add(x,y):      return(x+y)
def multiply(x,y): return(x*y)
def is_even(x):    return((x % 2) == 0)
def is_odd(x):     return((x % 2) != 0)










##########  Higher-Order Functions (1)  ##########
# MAKE_REGEX_PARSER returns an object's function

def validate(value, value_type, parse):
	if(parse(value)): return(value + ":  Valid "   + value_type)
	else:             return(value + ":  Invalid " + value_type)

def make_regex_parser(regex_string):
	return re.compile(regex_string).match

parse_address = make_regex_parser(r"(?i)^\d+[a-z\. ]+$"    )
parse_name    = make_regex_parser(r"(?i)^[a-z]+( [a-z]+)?$")

test("Higher-Order Functions (1)",
	validate("123 Main St.",        "Address", parse_address),
	validate("Main St.",            "Address", parse_address),
	validate("Joe Mama",            "Name",    parse_name   ),
	validate("Joe 'l33tz0rs' Mama", "Name",    parse_name   ),
)










##########  Higher-Order Functions (2)  ##########
# the MAKE_ADDER functions create & return new functions

def make_adder_lambda(y):
	return(lambda x: x + y)

def make_adder_def(y):
	def adder(x):
		return(x + y)
	return(adder)

add_10 = make_adder_lambda(10);
add_20 = make_adder_lambda(20);
add_30 = make_adder_def(30);
add_40 = make_adder_def(40);

test("Higher-Order Functions (2)",
	add_10(0),
	add_20(0),
	add_30(0),
	add_40(0),
)










##########  Clojures  ##########
# FUNCTION_A utilizes nested higher-order functions
# in order to store arguments in parent functions
# where they remain accessible to child functions

def function_A(a1):
	a2 = "1-2"
	def function_B(b1):
		b2 = "2-2"
		def function_C(c1):
			c2 = "3-2"
			return[[a1, a2], [b1, b2], [c1, c2]]
		return function_C
	return function_B

function_B = function_A("1-1")
function_C = function_B("2-1")

test("Clojures",
	function_C("3-1"),
	function_B("###")("$$$"),
	function_C("@@@"),
	function_A("100")("200")("300"),
)










##########  Point-Free Notation, Function Composition, Currying  ##########
# SQUARE_ADD5_MULTIPLY10_X passes its arguments to [SQUARE, ADD, MULTIPLY]
# with the result of each function being passed to the next
#
# this functionality requires the syntax_sugar module:

# explicit
square_add5_multiply10_1 = pipe() | square | F(add, y=5) | F(multiply, y=10)
# implicit
square_add5_multiply10_2 = pipe() | square | (add, 5) | (multiply, 10)

test("Point-Free Notation, Function Composition, Currying",
	square_add5_multiply10_1(3),
	square_add5_multiply10_1(3),
	square_add5_multiply10_2(5),
	square_add5_multiply10_2(5),
)










##########  Partial Application  ##########
# BRACKET & DOUBLE_BRACKET utilize FUNCTOOLS.PARTIAL
# to create new functions from BRACKET_BASE where
# the PREFIX & SUFFIX arguments are already defined

def bracket_base(x, prefix, suffix):
	return(prefix + x + suffix)

bracket        = F(bracket_base, prefix="{",  suffix="}" )
double_bracket = F(bracket_base, prefix="{{", suffix="}}")

test("Partial Application",
	bracket("Joe"),
	double_bracket("Joe"),
)










##########  Map  ##########
# NUMBERS_MULTIPLY10 & NUMBERS_MULTIPLY20 utilize MAP in order to create
# mutated copies of NUMBERS without affecting the original data

numbers = [1, 2, 3, 4, 5]
numbers_multiply10 = map(lambda x: x * 10,  numbers)
numbers_multiply20 = map(F(multiply, y=20), numbers)

test("Map",
	list(numbers_multiply10),
	list(numbers_multiply20),
	list(numbers),
)










##########  Filter  ##########
# ODD_NUMBERS & EVEN_NUMBERS utilize FILTER in order to extract
# specific items from NUMBERS without affecting the original data

numbers = [1, 2, 3, 4, 5]
odd_numbers  = filter(is_odd,  numbers)
even_numbers = filter(is_even, numbers)

test("Filter",
	list(odd_numbers),
	list(even_numbers),
	numbers,
)










##########  Reduce  ##########
# NUMBERS_SUM_1 & NUMBERS_SUM_2 utilize FUNCTOOLS.REDUCE
# in order to pass each value in NUMBERS_X to ADD
#
# the first iteration calls ADD(X=NUMBERS_X[0], Y=NUMBERS_X[1]),
# and each subsequent iteration calls ADD(PREVIOUS_RESULT, NUMBERS_X[i])

numbers_1 = [1, 2, 3]
numbers_2 = [1, 2, 3, 4, 5]
numbers_sum_1 = reduce(add, numbers_1)
numbers_sum_2 = reduce(add, numbers_2)

test("Reduce",
	numbers_sum_1,
	numbers_sum_2,
	numbers,
)