CS 214 Lab 10: Lambda Functions in Emacs-LISP

Since emacs LISP is not object oriented, we will spend our time this week looking at the unusual way Lisp handles functions, and the power this provides.

To begin, make a new eLisp subdirectory in your labs/10 directory, and launch emacs.

Part 1

Thus far, we have been creating named functions in eLisp using defun. However, defun is really just a means of assigning a name to a function. We can also create unnamed functions, using a Lambda abstraction. Lambda abstraction is the process of creating a function from a list of variables and a term. Its syntax is as follows:

   (lambda parameter-list term )
where lambda is a reserved word of the language, parameter-list is a parentheses enclosed list of parameter names (or the empty list if there are no parameters), and term is a syntactically correct LISP expression.

Using the above form, we can use a lambda expression to create a function that takes a single variable x as its argument, and returns twice the value of x:

   (lambda (x) (+ x x)   )

Type this expression into the emacs scratch buffer, and note what it returns as a comment in the buffer. This return value indicates that the expression returns a function. It is important to note that while the lambda expression returns a function, it is not itself a function. That is, LAMBDA is a syntactic operation that creates functions.

Since lambda expressions return functions, we can also then pass arguments to the functions that a lambda expression creates:

   ((lambda (x) (+ x x)) 17)
The lambda expression returns a function, which is then passed the value of 17 as its argument. Note the return value of the above expression as a comment in your scratch buffer.

Exercise 1a:

Write an expression that returns a function that finds the maximum of the absolute value of 4 numbers. Hint: absolute value (abs) and maximum (max) are built in functions in e-Lisp.

Exercise 1b:

For each of the following, use the function in an expression such that the function will be evaluated on a valid argument list. The argument list supplied should cause the function to be evaluated with no errors.

	(lambda (x y z) (+ x y z))
	(lambda (x) (null x))
	(lambda () 17.2)

Part 2

Functions are first order objects in LISP, which means that we can pass functions as arguments to other functions! In order to pass a function, we need a way to reference it. There are two possibilities:

So, for example, if we type:

   (function abs)

LISP will return the function associated with the name abs. The representation of the function is implementation dependent -- some versions of LISP provide much more information than eLisp.

The notation #' can be used as a shortcut for function. For example:

   #'abs
should produce the same result as (function abs).

To use this function, we can store a reference to it in a variable:

   (setq double (function (lambda (x) (+ x x)) ))
or
   (setq double '(lambda (x) (+ x x)) )
Now try calling
   (double 11)
This fails because double is not the definition of the lambda function; it is just a variable referring to the lambda function we defined.

To call the function to which double refers, you can use the funcall function:

   (funcall double 11)      ; should produce 22
The funcall function calls the function it is passed, passes it any arguments that are supplied, and returns the result.

Define a lambda function that triples its single argument, store a reference to that function in a variable named triple, and show that it works correctly. When it does, make that result a comment in your buffer and continue.

The power of this approach is that we can pass functions as arguments to other functions, and write functions that return functions as their result. The funcall function we just used is a function that takes another function as an argument, takes one or more arguments (as appropriate for the function), and invokes that function, passing it the arguments.

Another way to use this is using the apply function. It takes a function and a list of arguments for it, and returns the result of applying that function to each of the arguments. For example:

   (apply '+ '(1 2 3))    ; should return 6
The function being applied must be applicable to the argument(s), and the last argument to apply must be a list. For example:

   (apply '+ 1 2 '(3 4 5))
is valid, while
   (apply '+ 1 '(3 4 5) 2)
results in an error, because the final argument is not a list.

The funcall function is similar to apply, but the restriction on the final argument is lifted:

   (funcall '+ 1 2 3 4)
   (funcall 'cons 'a '(b))

Both apply and funcall are applicative operators. Probably the most frequently used applicative operator is mapcar, which applies a function to each element of a list, one at a time, and returns a list of the results. For example:

   (mapcar 'evenp '(1 2 3 4))
   (mapcar 'oddp '(1 2 3 4))
   (mapcar 'abs '(-1 2 -3 4 -5))

These applicative operators do not require named functions -- we can also use lambda expressions. For example:

   (defun subtract-n (n list)
   	(mapcar '(lambda (x) (- x n)) list))
What does this produce?
   (subtract-n 2 '(2 4 6))
The lambda functions in combination with the applicative operators thus provide a way to write very powerful in very concise notation.

Exercise 2:

Write a function sum-squares that computes the sum of the squares of the numbers in the list argument. Your definition should not use recursion (nor loops!), nor should it use setq to name any helper functions. Some examples:

   (sum-squares '(1 2 3))          ; should return 14
   (sum-squares '(1 2 3 4 5))      ; should return 55
   (sum-squares '(-1 -2 -3 0 1))   ; should return 15 

Finally, note that both Java-8 and C++11 have added support for lambda expressions, bringing the power of lambdas to these C-family languages.

Turn In. When your function executes correctly, add a comment at the top of the buffer providing your name, the date, what this is for, etc. Then save a copy of your buffer in a file named lab10.el (making sure that its correct output is present).

That concludes the e-Lisp part of this lab.

If this was your last part of the exercise, return to the lab 10 page and follow the "Turn In" instructions there.


Calvin > CS > 214 > Labs > 10 > e-Lisp


This page maintained by Joel Adams.