<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Some examples on functions in Python
In the previous class, we have already discussed functions, arguments, default values and kwargs. Here are some more examples.
Step1:  Moral
Step2:  Anonymous functions
Can create these on the fly
Also called lambda functions
Step3:  List comprehensions
Allow you to create lists using a set builder like notation
Find and store the squares of the first N whole
numbers
Step4:  Example
Step5:  Now we form a new list with the sum and the actual tuples linked together in a tuple
Step6:  Finally, we create a dictionary with the cube as the key and the (a, b) tuple pairs in a list as the values
Step7:  Functions within functions
You can define a function within another
<ASSISTANT_TASK:>
Python Code:
def swap(a, b):
    a, b = b, a
x, y = 1, 2
print("Before swap, x = %d and y = %d." % (x, y))
swap(x, y)
print("After swap, x = %d and y = %d." % (x, y))
Explanation: Some examples on functions in Python
In the previous class, we have already discussed functions, arguments, default values and kwargs. Here are some more examples.
End of explanation
def add_function_of_integers(func, upto):
    total = 0
    for n in range(upto + 1):
        total = total + func(n)
    return total
def identity(n):
    return n
N = 10
print("Sum of integers up to %d is %d." %
      (N, add_function_of_integers(identity, N)))
def square(n):
    return n * n
print("Sum of the squares of integers up to %d is %d." %
      (N, add_function_of_integers(square, N)))
Explanation: Moral: functions are pass-by-value.
Functions are objects!
Functions are first class objects
You can pass them around as arguments to other functions or assign them to variables
End of explanation
N = 3
print("Sum of cubes of integers up to %d is %d." %
      (N, add_function_of_integers(lambda x : x**3, N)))
#def cube(x):
#    return x**3
cube = lambda x : x**3
cube(10)
add_two_numbers = lambda x, y : x + y
add_two_numbers(10, 20)
# To find the sum 1 + 1 + 1 ... + 1
print("Sum of the %d ones is %d." %
      (N + 1, add_function_of_integers(lambda x : 1, N)))
Explanation: Anonymous functions
Can create these on the fly
Also called lambda functions
End of explanation
n_squares = []
for i in range(N + 1):
    n_squares.append(square(i))
print(n_squares)
whole_numbers = range(N + 1)
n_cubes = [i * i * i for i in whole_numbers]
n_cubes
N = 6
# Express dice rolls as (first die face, second die face)
cartesian_product = [(a, b)
                     for a in range(1, 7)
                     for b in range(1, 7)]
print(cartesian_product)
odd_sum_cases = [(a, b)
                     for a in range(1, 7)
                     for b in range(1, 7)
                if (a + b) % 2 == 1]
print(odd_sum_cases)
list_of_numbers = range(11)
odd_squares = [i * i
               for i in list_of_numbers
               if i % 2 == 1]
print(odd_squares)
# Using map
list_of_squares = list(map(lambda x : x * x,
                     list_of_numbers))
print(list_of_squares)
# We will use filter on the dice pairs to print
# only those that sum to an even number
even_sum = filter(lambda x :
                       (x[0] + x[1]) % 2 == 0,
                  cartesian_product)
for die_rolls in even_sum:
    print("%d and %d sum to even!" %
          (die_rolls[0], die_rolls[1]))
print("Let's try printing it again!")
for die_rolls in even_sum:
    print("%d and %d sum to even!" %
          (die_rolls[0], die_rolls[1]))
Explanation: List comprehensions
Allow you to create lists using a set builder like notation
Find and store the squares of the first N whole
numbers
End of explanation
pairs_of_numbers = [(a, b) for a in range(1, 21)
                   for b in range(1, 21)]
pairs_of_numbers = list(filter(lambda x: x[0] <= x[1],
                              pairs_of_numbers))
print(pairs_of_numbers)
Explanation: Example: Ramanujan number
Find the smallest number that can be expressed as the cube of two natural numbers in two different ways
End of explanation
numbers_with_cube_sums = [(a[0]**3 + a[1]**3,a)
                         for a in pairs_of_numbers]
print(numbers_with_cube_sums)
Explanation: Now we form a new list with the sum and the actual tuples linked together in a tuple
End of explanation
#dict_of_cube_sums = {}
#for i in numbers_with_cube_sums:
#    if i[0] in dict_of_cube_sums:
#        dict_of_cube_sums[i[0]].append(i[1])
#    else:
#        dict_of_cube_sums[i[0]] = [i[1]]
dict_of_cube_sums = {}
[dict_of_cube_sums.setdefault(i[0], [])
for i in numbers_with_cube_sums]
[dict_of_cube_sums[i[0]].append(i[1])
for i in numbers_with_cube_sums]
print(dict_of_cube_sums)
# Find the elements which have more than one element
# in their list
ramanujan_candidates = list(
    filter(lambda i : len(i[1]) > 1,
        dict_of_cube_sums.items()))
ramanujan_candidates
Explanation: Finally, we create a dictionary with the cube as the key and the (a, b) tuple pairs in a list as the values
End of explanation
def add_n(n):
    def adder(a):
        return a + n
    return adder
add10 = add_n(10)
print(add10(23))
add3 = add_n(3)
print(add3(23))
def logger(func):
    def inner(*args, **kwargs):
        print("Arguments: %s %s" % (args, kwargs))
        return func(*args, **kwargs)
    return inner
def add_two_numbers(x, y):
    return x + y
print("Adding %d and %d gives %d" % (1, 3,
                                    add_two_numbers(1, 3)))
logged_add_two_numbers = logger(add_two_numbers)
mysum = logged_add_two_numbers(1, 3)
print(mysum)
Explanation: Functions within functions
You can define a function within another
End of explanation
<END_TASK>