Presentation on theme: "Functional Programming COMP2003 A course on functional programming using Common Lisp Dr Eleni Mangina"— Presentation transcript:
Functional Programming COMP2003 A course on functional programming using Common Lisp Dr Eleni Mangina firstname.lastname@example.org
Passing functions as arguments APPLY and FUNCALL LAMBDA Local Variables LET LET & LAMBDA LET *
APPLY and FUNCALL These two functions take a function and a set of arguments, and apply the first to the second. The only difference between them is the way in which they expect the set of arguments to be arranged – funcall expects here to be as many items as necessary
> (funcall #’* 2 3 4) 24 apply takes a list of items that the function is to be applied to: > (apply #’* ‘(2 3 4)) 24 > (apply #’+ ‘(5 2 6 4)) 17 > (apply #’+ 5 ‘(2 6 4)) 17 > (apply #’+ 5 2 ‘(6 4)) 17 > (apply #’+ 5 2 6 4 ()) 17 APPLY and FUNCALL (cont.)
The apply-append trick Suppose we have a list S of lists and we wish to join all the lists in S end-to-end. For example suppose S is: ((a f g) (c d) ( ) (p q r)) and we want a function that wil essentially “remove the brackets”to give: (a f g c d p q r) All we need is: (apply #’append S)
LAMBDA So far, we have seen two places in which a symbol can be used to refer to a function: 1.As the first item in a list representing a function call: (append x 12) 2.With (function …) wrapped round it when acting as the argument to another function: (apply #’* (find-numbers)) What can actually appear in these positions is either a symbol which is the name of a function or a description of an unnamed function. The latter effect is achieved using a lambda definition. A lambda definition is just like an ordinary function definition, except that: There is no function name specified The word lambda is used instead of defun
LAMBDA (lambda (ls) (first (last ls)) > ((lambda (ls) (first (last ls))) ‘(a b c d e)) e (funcall #’(lambda (ls) (first (last ls))) ‘(a b c d e)) e (apply #’(lambda (ls) (first (last ls))) ‘((a b c d e))) e Note: 1. The #’ is not part of the lambda definition, but has to be stuck on the front just when the lambda expression is being passed as an argument to another function 2. When the lambda expression appears in the privileged first position, it does not need the #’, just as the symbolic name of a function does not need it; however, when the lambda expression appears as an argument to another function, it does need the #’.
>((lambda (A B) (* (+ A B) (-A B) ) ) 10 3) 91 >(funcall #’ (lambda (A B) (* (+ A B) (-A B) ) ) 10 3) 91 >(apply #’ (lambda (A B) (* (+ A B) (-A B) ) ) ‘(10 3)) 91
What is lambda for: 1.When the function definition is specific to a particular situation, and so can be defined and used in just one place. Usually the motive for forming the computation into a function is that it is being passed in as argument to some other functions which will use that computation in various ways, probably applying it repeatedly 2.When there is a need to manipulate variable names so that certain variables are “in scope” at the right time 3.When a function is constructing a function which it will then return as its result; that is, the result of some computation is not merely a data-structure, but is a new function definition which can then be passed on and used.
Consider this EXAMPLE: > (defun fncomp (fn1 fn2) #’ (lambda (val) (funcall fn1 (funcall fn2 val)) ) > (setf g (fncomp #’first #’rest)) > (funcall g ‘(a b c d)) b
LET Although strict functional programming does not allow the use of assignment to local variables, Common Lisp has a command which gives a limited (but very useful) kind of variable declaration and initialization which does not violate the principles of functional programming. The let command: –Declares one or more local variables –Optionally gives them initially values –Executes a sequence of S-expressions using these settings
The general format of LET command: (let (( ) ( ) … ( ) ) … )
The effect of the LET statement is to declare all these s, evaluate each of the s, assigning the results to the corresponding s, and to evaluate the “body” of the statement, returning as the result of his computation whatever is last evaluated inside the body. At the end of this evaluation, processing continues outside the LET statement, and the variables declared inside it are no longer accessible. For example:
LET and LAMBDA A LET command can be thought of as directly equivalent to the use of a lambda expression. A LET statement, introduces variables, gives them initial values, evaluates some expressions, then discards the variables; this is essentially what happens when a lambda function is defined and then used immediately Compare: (let ((x 3) (y 4)) (+ (*x y) (+ x y)) With: ((lambda (x y) (+ (*x y ))) 3 4)
Hence, using LET does not go beyond the principles of functional programming, since it is simply a handy notation for defining and then immediately using a LAMBDA expression. However, do not use LAMBA function calls simply to introduce and bind local variables – the examples here are merely to illustrate a conceptual point. The natural way to introduce local variables and give them initial values is the LET command; trying to do it with LAMBDA will be rather complicated.
LET* In the LET command the initial values of variables are all assigned simultaneously, so the initialization of one variable cannot assume the value of one of the other variables in the same header. Hence in: (let ((x (rest y)) (z (first x)) ) … ) The expression (first x) will refer to some x which is available, outside the LET command, not to the x which is alongside it in the LET statement
LET* The LET* introduces and initializes the variables one after another (sequentially) so that variables can be used as soon as they are set up, even in further initializations: > (setf x 3) 3 > (let ((a x) (x 5) (b x)) (list a x b)) (3 5 3) > (let* ((a x) (x 5) (b x)) (list a x b)) (3 5 5)
Bound and free variables: The LET & LAMBDA constructs are examples of statements which can introduce new variables with a clearly defined (and limited) scope. A variable is said to be bound within the area of program to which it belongs; in the case of LET command, that area is what is called the “body” of the LET command. Any variable mentioned in a piece of program which is not “bound”within hat piece of program text is said to be free in that text. For example consider the following function:
(defun print-pers-nfo (person) (let ((name (getname person)) (address (getaddress person)) ) (cond ((or (newmember name) (overseas address)) (print name) (print address) ) If we were to consider the LET block in isolation, the variable person is free with respect to that block, but it is bound with respect to the function print-pers-info.
LEXICAL SCOPING Common Lisp relies on what is known as lexical scoping which means, roughly, that a variable may be referred to at any point in the program which is textually enclosed by the construct which introduced the variable. If there are several enclosing statements and if two of these have introduced variables with the same name, it is the innermost one which is taken as being referred to. Try the next examples:
>(let((x 6) (y 7)) (print x) (print y) (let((x 10) (z 8)) (print x) (print y) (print z) ) (print x) ) > 6 7 10 7 8 6 Similar nesting rules apply if there is a lambda function inside another lambda function