#!/usr/bin/env python # coding: utf-8 # [Home](Home.ipynb) # # # Lambda Expressions # # # ### Bigger Picture # # The Lambda Calculus arose under a bevy of thinkers, at the Institute for Advanced Study at Princeton and elsewhere, to formalize the algebra of composing and defining callables. Recursion is key. # # In the context of our subculture, Lambda Calculus may increase its scope to define a track parallel to Delta Calculus through high school. What used to be the college prep and vocational tracks (shop) has morphed into $\Delta$ versus $\Lambda$ calculus, with the latter being "computer science" and/or "discrete math" in disguise. # ### Python in Particular # "Mary had a little lambda" we Pythonistas sometimes say, reminding us of the nursery rhyme, but also the Python's lambdas are little. # # "If you need more than a one liner, you need a named object" is the implicit advice. In other words, the so-called anonymous function is for expressing simple, primitive operations. # The lambda expression below gets passed as a first argument to map. map expects a callable and an iterable as arguments, and outputs a map type with "just in time" characteristics. Converting a map to a list causes every element to evaluate and we get a final result. # In[1]: list(map(lambda x: x//2, range(10))) # Map expects a callable that eats a single positional object. You may nevertheless break those objects apart in the body of the callable, including within a lambda expression. # # Fraction expects two arguments, numerator and denominator. The star in front of each tuple *t* e.g. (1, 2) then (1, 3), breaks each tuple into two separate positional arguments. # In[4]: from fractions import Fraction list(map(lambda t: Fraction(*t), ((1,2),(1,3),(3,7),(5,12)))) # Python does include a version of map, called starmap, that "explodes" each object as it comes in, into separate positional arguments. # # Imagine passing ```*(1,2)``` instead of just ```(1,2)```. That implies consuming two objects instead of one. # In[16]: # a short sketch def f(a,b): """ eats two objects """ print(f"a={a} b={b}") return a + b try: f((1,2)) except: print("Wrong number of args") try: f(*(1,2)) # cracks a 2-tuple in two except: print("Bad news") else: print("All good") # And now for ```starmap``` where the star in question is the exploding asterisk. We're now able to craft an intake that eats two objects (in the examples below) or more. # In[12]: from itertools import starmap # In[17]: list(starmap(f, ((3, 4),(5,6)))) # In[18]: list(starmap(lambda n,d: Fraction(n,d), ((1,2),(1,3),(3,7),(5,12)))) # You might call a lambda expression an "eater". # # Try mentally replacing the word lambda with the word eat, and see the expression after the colon as "doing the thing" (some operation, some procedure) with whatever's eaten. # # The purpose of the eater is to digest (perform a transformation) and give back (evaluate to a result). # ### Truth Testing # # In other Notebooks, we explore some theorems from Number Theory, namely Fermat's Little Theorem and Euler's Totient Theorem. # # Fermat's Little Theorem says: # # $(a^{p} - a) \mid p$ where $p$ is prime and $a$ is any base. # # In other words, any prime $p$ divides $(a^{p} - a)$ without remainder. # In[20]: from primes import eratosthenes # In[22]: the_primes = eratosthenes(100) # In[32]: print( list(filter( lambda p: divmod(4**p - 4, p)[1] == 0, the_primes)) ) # In[ ]: