Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Higher order programming with Lisp: Passing a function to mapcar?

I'm just learning ANSI Common Lisp (using clisp on a Win32 machine) and I was wondering if mapcar could use a function passed in as a formal argument? Please see the following:

(defun foo (fn seq) 
    (mapcar #'fn seq))

This would, in my opinion, provide a greater level of flexibility than:

(defun mult (i)
    (* i 2))

(defun foo ()
    (mapcar #'mult '(1 2 3)))
like image 458
CaitlinG Avatar asked Jan 08 '12 03:01

CaitlinG


People also ask

What is Mapcar Lisp?

mapcar is a function that calls its first argument with each element of its second argument, in turn. The second argument must be a sequence. The ' map ' part of the name comes from the mathematical phrase, “mapping over a domain”, meaning to apply a function to each of the elements in a domain.

What is higher order procedure in Lisp?

One of the most powerful techniques that Lisp and other functional programming languages provide is the ability to define functions that take other functions as parameters or return them as results. These functions are called higher-order functions and are an important tool for procedural abstraction.

How do you reverse a function in Lisp?

In ANSI Common Lisp, you can reverse a list using the reverse function (nondestructive: allocates a new list), or nreverse (rearranges the building blocks or data of the existing list to produce the reversed one).

How recursion is implemented in Lisp?

In pure Lisp there is no looping; recursion is used instead. A recursive function is defined in terms of: 1. One or more base cases 2. Invocation of one or more simpler instances of itself.


2 Answers

This is absolutely possible! You're almost there. You just bumped into Common Lisp's dual namespaces, which can take a lot of getting used to. I hope I can say a thing or two to make Common Lisp's two namespaces a bit less confusing.

Your code is almost correct. You wrote:

(defun foo (fn seq) 
    (mapcar #'fn seq))

But, what is that trying to do? Well, #' is shorthand. I'll expand it out for you.

(defun foo (fn seq) 
    (mapcar (function fn) seq))

So, #'symbol is shorthand for (function symbol). In Common Lisp -- as you seem to know -- symbols can be bound to a function and to a variable; these are the two namespaces that Lispers talk so much about: the function namespace, and the variable namespace.

Now, what the function special form does is get the function bound to a symbol, or, if you like, the value that the symbol has in the function namespace.

Now, on the REPL, what you wrote would be obviously what you want.

(mapcar #'car sequence)

Would map the car function to a sequence of lists. And the car symbol has no variable binding, only a function binding, which is why you need to use (function ...) (or its shorthand, #') to get at the actual function.

Your foo function doesn't work because the function you pass it as an argument is being bound to a symbol as a variable. Try this:

(let ((fn #'sqrt))
    (mapcar #'fn '(4 9 16 25)))

You might have expected a list of all those numbers' square roots, but it didn't work. That's because you used let to bind the square root function to fn as a variable. Now, try this code:

(let ((fn #'sqrt))
    (mapcar fn '(4 9 16 25)))

Delightful! This binds the square root function to the fn symbol as a variable.

So, let's go revise your foo function:

(defun foo (fn seq) 
    (mapcar fn seq))

That will work, because fn is a variable. Let's test it, just to make sure:

;; This will not work
(foo sqrt '(4 9 16 25))
;; This will work
(foo #'sqrt '(4 9 16 25))

The first one didn't work, because the square root function is bound to sqrt in the function namespace. So, in the second one, we grabbed the function from the symbol, and passed it to foo, which bound it to the symbol fn as a variable.


Okay, so what if you want to bind a function to a symbol in the function namespace? Well, for starters, defun does that, permanently. If you want it to be temporary, like a let binding, use flet. Flet is, in my opinion, stupid, because it doesn't work exactly like let does. But, I'll give an example, just so you can see.

(flet ((fn (x) (sqrt x)))
    (mapcar fn '(4 9 16 25)))

will not work, because flet didn't bind the function to the symbol in the variable namespace, but in the function namespace.

(flet ((fn (x) (sqrt x)))
    (mapcar #'fn '(4 9 16 25)))

This will do what you expect, because flet bound that function to the symbol fn in the function namespace. And, just to drive the idea of the function namespace home:

(flet ((fn (x) (sqrt x)))
    (fn 16))

Will return 4.

like image 51
Daniel Ralston Avatar answered Sep 22 '22 05:09

Daniel Ralston


Sure, you can do this:

(defun foo (fn)
  (mapcar fn '(1 2 3)))

Examples:

(foo #'(lambda (x) (* x 2)))
(foo #'1+)
(foo #'sqrt)
(foo #'(lambda (x) (1+ (* x 3))))
like image 38
Chris Jester-Young Avatar answered Sep 24 '22 05:09

Chris Jester-Young