I'm learning lisp and I have to return modified input arguments from my function in Lisp.
Consider this easy example:
(defun swap (l1 l2)
(let ((temp))
(setf temp l1)
(setf l1 l2)
(setf l2 temp)))
(setf a (list 1 2 3))
(setf b (list 7 8 9))
(swap a b)
(print a)
(print b)
It doesn't work because I don't know how to pass reference to variable to function. Is that even possible in lisp? How can this function be solved?
UPDATE
;;; doesn't change original
(defun foo1 (x)
(setf x (list 0 0 0)))
;;; but this does
(defun foo4 (x)
(setf (car x) 0)
(setf (cdr x) (list 0 0)))
The reason why I wanted to pass a variable by reference to be able to change it is that, when I have function with 3 input arguments and that function should change all of them, I think it is more elegant to change them by reference, then return list of three variables and then overwrite with them original variables:
;;; more elegant function
(defun foo (x y z)
;;... some work ...
;; Lets PRETEND this does work
(setf x new-x)
(setf y new-y)
(setf z new-z))
; after this, a,b,c will have new values
(foo a b c)
;;; less elegant function
(defun foo (x y z)
;; ... some work ...
(list new-x new-y new-z))
; after this, I still will have to manually set a,b,c
(setf temp (foo a b c))
(setf a (nth 0 tmp))
(setf b (nth 1 tmp))
(setf c (nth 2 tmp))
To explain why I'm trying to accomplish this is I got Hanoi towers homework. I was thinking about using three lists as stacks
and use pop
and push
functions on them to insert and remove the "discs". I defined (move n source target temp)
function, and it is recursively calling itself with n-1
change. The problem is, when I pop
or push
stack in recursive function, it doesn't influence stacks outside.
If I want my move
function to return stacks after n
movements, should I really return list of new stacks (that less elegant function) instead of editing them by reference (that more elegant function)
What is the proper way in Functional languages?
First of all, if you are learning functional programming or Lisps in general, and not just Common Lisp, don't do it. Don't try to write functions that modify state - it's not the way functional programming works. If you ever need function that exchanges 2 values, just write functions that return them in reverse order.
If you are still interested in exchanging 2 values, see this similar question for several very good suggestions. Most important are macros and manual references (wrappers for actual values).
However these answers don't include one important concept, available only in Common Lisp and not most other Lisp dialects - places. But first let's recall 2 ways to pass variable to function. Consider following example in C++:
void f(int x) {
...
}
int a = 5;
f(a);
This is known as "pass-by-value" strategy: value of a
is copied to parameter x
. And since x
is just a copy, if you modify it inside of f()
, nothing will happen with original variable a
.
However, in C++ you can also do the following:
void f(int& x) {
...
}
int a = 5;
f(a);
This strategy is called "pass-by-reference" - here you pass pointer to location in memory where a
resides. Thus x
and a
point to the same piece of memory, and if you modify x
, a
will change too.
Functional languages, including Common Lisp, don't allow you to pass variables to functions by reference. So how setf
works? It turns out that CL has concept of place (sometimes also referred as "location") that defines location in memory. setf
(macro that is expanded to set
special form) works directly with places and not values.
To summarize:
setf
works directly with places and can be used to mutate variables. Macros may be used to overcome limitations of functions. Note, that some built-in functions in CL can return places, e.g. car
, cdr
, aref
and also all object accessors. See this pages for some examples.
UPDATE
Your new question is where to modify values - inside the function by reference or outside without references. However, none of these would be correct in functional programming. Right answer here is: don't modify anything. In FP you normally have some state variable(s), but instead of modifying it in place you create modified copy and pass it further, so that original variable is not changed. Consider example of recursive function for calculating factorial:
(defun factorial-ex (x accum)
(if (<= x 1)
accum
(factorial-ex (- x 1) (* x accum))))
(defun factorial (x)
(factorial-ex x 1))
factorial-ex
is auxiliary function that takes one more parameter - accumulator to hold current state of calculation. On each recursion call we decrease x
by 1 and multiply accum
by current value of x
. However, we don't change values of x
and accum
- we pass new values into recursive call to the function. Physically there are many copies of x
and accum
- one for each function call - and none of them ever changes.
(Note, that some CL implementations with particular options could use so-called tail call optimization that breaks statement about different locations in memory above, but at the moment you shouldn't worry about it.)
In your task you can do the same thing. Instead of modifying your 3 variables - either inside or outside function - make their modified copies and pass them to recursive call. In imperative programming you use variables and loops, and in functional programming you should prefer immutable values and recursion.
The builtin macro rotatef
fulfills this functionality:
(setf x 1)
(setf y 3)
;x = 1, y = 3
(rotatef x y)
;x = 3, y = 1
In order to write your own function to do this, I would recommend creating a macro:
(defmacro my-swap (a b)
`(let ((temp ,a))
(setf ,a ,b)
(setf ,b temp)))
However, as Clayton pointed out this macro will fail if it is applied to a variable named "temp". Hence, we can use gensym
to create a new variable name (guaranteed to not be in use) and pass that to a secondary macro which actually switches the values:
(defmacro my-swap-impl (a b sym) ;;implementation of my-swap
`(let ((,sym ,b)) ;evaluate the symbol and use it as a variable name
(setf ,b ,a)
(setf ,a ,sym)))
This is a version of the previous swap macro that accepts a third argument to serve as the temporary variable name. This is called from a simple macro:
(defmacro my-swap (a b) ;;simply passes a variable name for use in my-swap-impl
`(my-swap-impl ,a ,b ,(gensym)))
This setup can be used exactly like the previous one except that it is safe from variable capture.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With