Computer Science 15-112, Spring 2013
Class Notes:  More on Functions


  1. Function Documentation Strings
  2. Parameter Lists
    1. Default Argument Values
    2. Keyword Arguments
    3. Variable-Length or Arbitrary Argument Lists
    4. Unpacking Argument Lists
  3. First-class Functions (Functions as first-class data types)
    1. Custom Comparators
    2. Callback functions (say, in Tkinter event handling)
  4. Lambda Expressions (Anonymous functions)
  5. Nested Function Definitions and Closures
  6. Case Study: Global-Free Tkinter Animations
  7. More Topics (not covered this semester)

  1. Function Documentation Strings
    1. Using doc strings
      print max.__doc__
      
      # or, if you prefer...
      
      from pydoc import help
      help(max)
    2. Creating doc strings
      def isNumeric(n):
          """Returns True if n is a numeric type, and False otherwise."""
          return type(n) in (int, long, float, complex)
      
      def getNumeric(n):
          """Returns n if it is numeric, or None otherwise."""
          return n if isNumeric(n) else None
      
      def larger(x,y):
          """Returns the larger value of x and y if both are numeric.
          Non-numeric values are treated as None."""
          return max(getNumeric(x), getNumeric(y))
      
      print isNumeric.__doc__
      print larger.__doc__
      print larger(42, "amazing!")

    Parameter Lists

    1. Default Argument Values
      1. Simple Example
        def repeat(msg, times=5):
            for i in xrange(times):
                print msg,
            print
        
        repeat("wow")        # wow wow wow wow wow
        repeat("amazing", 2) # amazing amazing
      2. Warning: Default values evaluated only once (so mutable lists accumulate values!)
        def seen(value, seenSet=set()):
            # return True if value has been seen, False otherwise.
            # Either way, add value to the seenSet
            result = value in seenSet
            seenSet.add(value)
            return result
        
        print seen(3) # False
        print seen(4) # False
        print seen(4) # True
        
        mySet = set()
        print seen(4, mySet) # False
        print seen(4, mySet) # True
        print seen(4)        # True (still)
    2. Keyword Arguments
      def repeat(msg, times=5, onePerLine=True):
          for i in xrange(times):
              print msg,
              if (onePerLine): print
          if (not onePerLine): print
      
      repeat("wow", times=2)
      repeat("wow", times=2, onePerLine=False)
    3. Variable-Length or Arbitrary Argument Lists
      1. *args
        def longestWord(*args):
            if (len(args) == 0): return None
            result = args[0]
            for word in args:
                if (len(word) > len(result)):
                    result = word
            return result
        
        print longestWord("this", "is", "really", "nice") # really
      2. **kwargs
         
    4. Unpacking Argument Lists
      1. *args
        def hasRoot(a, b, c):
            """Returns True if y=ax**2+bx+c has a root (crosses the x axis)."""
            return (b**2 - 4*a*c >= 0)
        
        coeffs = (1,3,-5)
        print hasRoot(coeffs[0], coeffs[1], coeffs[2])
        
        (a,b,c) = coeffs
        print hasRoot(a,b,c)
        
        print hasRoot(coeffs) # crashes!
        
        print hasRoot(*coeffs)
      2. **kwargs
         
  2. First-class Functions (Functions as first-class data types)
    1. Custom Comparators
      def compareLengths(a, b):
          return len(a) - len(b)
      
      def compareLengthsByAlpha(a, b):
          if (len(a) != len(b)):
              return len(a) - len(b)
          else:
              return cmp(a, b)
      
      names = ["fred", "wilma", "barney", "betty", "dino"]
      
      print sorted(names)                        # ['barney', 'betty', 'dino', 'fred', 'wilma']
      print sorted(names, compareLengths)        # ['fred', 'dino', 'wilma', 'betty', 'barney']
      print sorted(names, compareLengthsByAlpha) # ['dino', 'fred', 'betty', 'wilma', 'barney']
    2. Callback functions (say, in Tkinter event handling)
      root.bind("<Key>", keyPressed)
  3. Lambda Expressions (Anonymous functions)
    1. Simple Example
      f = lambda x,y:  x+y
      print f(2,3)  # 5
    2. Python Limitation (only expressions, not statements)
      f = lambda x:  print x  # syntax error!
  4. Nested Function Definitions and Closures
    1. Simple Example
      def f(a):
          def evens(a):
              return [value for value in a if (value % 2) == 0]
          return list(reversed(evens(a)))
      
      print f(range(10))     # [8, 6, 4, 2, 0]
      print evens(range(10)) # NameError: 'evens' not defined!
    2. Example using a closure (non-local "free" variable defined in enclosing scope)
      def makeAdderFn(delta):
          def f(x):
              return x + delta
          return f
      
      add3 = makeAdderFn(3)
      add4 = makeAdderFn(4)
      
      print add3(5) # 8
      print add4(5) # 9
      
      # once again, with a lambda function
      
      def makeAdderFn(delta):
          return lambda x : x + delta
      
      add3 = makeAdderFn(3)
      add4 = makeAdderFn(4)
      
      print add3(5) # 8
      print add4(5) # 9
    3. Closures are read-only (until Python 3's "nonlocal" variables)
      def makeIncreasingAdderFn(delta):
          def f(x):
              delta += 1        # does not work!
              return x + delta
          return f
      
      f = makeIncreasingAdderFn(3)
      print f(5)
      print f(5)
    4. Mutable closure workaround (use a mutable data structure such as a List or Struct)
      1. List example
        def makeIncreasingAdderFn(delta):
            deltaList = [delta] # hack, but it works
            def f(x):
                deltaList[0] += 1
                return x + deltaList[0]
            return f
        
        f = makeIncreasingAdderFn(3)
        print f(5)  # 9
        print f(5)  # 10
      2. Struct example
        def makeIncreasingAdderFn(delta):
            class Struct: pass
            scope = Struct()
            scope.delta = delta   # this is cleaner
            def f(x):
                scope.delta += 1
                return x + scope.delta
            return f
        
        f = makeIncreasingAdderFn(3)
        print f(5)  # 9
        print f(5)  # 10
  5. Case Study: Global-Free Tkinter Animations
  6. More Topics (not covered this semester)
    1. Recursion (soon!)
    2. Higher-Order Functions (Functions over functions and/or returning functions)
      1. map/reduce/filter
        1. Python Limitation
          (map/reduce return lists; parenthesized list comprehensions return generators)
          (map/reduce cannot work on infinite streams, say itertools.count())
      2. Currying (from functools import partial as curry)
    3. Lazy Evaluation with Generators (itertools.count()) and generator functions (yield)
    4. Function Decorators
    5. The Tao of Functional Programming
      • No mutable values or side effects
        1. Calling the same function with the same arguments always produces the same result
        2. Supports verification, parallelization, thread-safety, even optimization (say, with memoization)

 carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem