Lisp argument pointer

I am learning lisp and I need to return the modified input arguments from my function in Lisp.

Consider this simple 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 ab) (print a) (print b) 

This does not work because I do not know how to pass a variable reference to a function. Is this 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 I wanted to pass the variable by reference in order to be able to change it is because when I have a function with 3 input arguments, and this function should change all of them, I think it’s more elegant to change the link, then return a list of three variables and then rewrite the original variables with them:

 ;;; more elegant function (defun foo (xyz) ;;... 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 abc) ;;; less elegant function (defun foo (xyz) ;; ... some work ... (list new-x new-y new-z)) ; after this, I still will have to manually set a,b,c (setf temp (foo abc)) (setf a (nth 0 tmp)) (setf b (nth 1 tmp)) (setf c (nth 2 tmp)) 

To explain why I am trying to do this, I got homework in Hanoi. I thought about using the three lists as stacks and used the pop and push functions for them to insert and remove the “drives”. I defined a function (move n source target temp) and it calls itself recursively with a change of n-1 . The problem is that when I pop or push stack in a recursive function, it does not affect the stacks outside. If I want my move function to return stacks after n moves, should I really return a list of new stacks (which is a less elegant function) instead of editing them by reference (which is a more elegant function)

What is the correct way in functional languages?

+4
source share
5 answers

First of all, if you are studying functional programming or Lisps in general, and not just Common Lisp, do not . Do not try to write functions that change state - this is not how functional programming works. If you need a function that changes 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 a few very good suggestions. The most important are macros and reference links (wrappers for actual values).

However, these answers do not include one important concept, available only in Common Lisp, and not in most other Lisp dialects - places . But first let me recall two ways to pass a variable to a function. Consider the following example in C ++:

 void f(int x) { ... } int a = 5; f(a); 

This is called a "move to value" strategy: the value of a copied to the parameter x . And since x is just a copy, if you change it inside f() , nothing will happen to the 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 a pointer to the location in memory where a is located. So x and a point to the same piece of memory, and if you change x , then t21 will also change.

Functional languages, including Common Lisp, do not allow variables to be passed to functions by reference. So how does setf work? It turns out that CL has the concept of place (sometimes also called "location"), which defines a location in memory, setf (a macro that is expanded to a special form set ), works directly with places, not values.

Summarizing:

  • Generic Lisp, like most Lisps, only allows variables to be passed by function only by value .
  • Lisp has the concept of places - a location in memory.
  • setf works directly with places and can be used to modify variables. Macros can be used to overcome feature limitations.

Note that some built-in functions in the CL may return places, for example. car , cdr , aref , as well as all object accessors. See these pages for some examples.

UPDATE

In your new question, you can change the values ​​- inside the function by reference or outside without links. However, none of them would be correct in functional programming. The correct answer here is: do not change anything . In FP, you usually have state variables, but instead of changing them, you create a modified copy and pass it on so that the original variable does not change. Consider an example of a recursive function to calculate the 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 an auxiliary function that takes another parameter - the accumulator for the current state of calculation. For each recursion call, we decrease x by 1 and multiply accum by the current value of x . However, we do not change the x and accum - we pass the new values ​​to the recursive function call. Physically, there are many copies of x and accum — one for each function call — and not one of them changes.

(Note that some CL implementations with certain parameters may use the so-called tail call optimization , which interrupts the statement about different places in the memory above, but at the moment you should not worry about that.)

In your task you can do the same. Instead of changing your 3 variables - inside or outside - make your modified copies and pass them to a recursive call. In imperative programming, you use variables and loops, and in functional programming, you should prefer fixed values ​​and recursion.

+9
source

The built-in rotatef macro performs this function:

 (setf x 1) (setf y 3) ;x = 1, y = 3 (rotatef xy) ;x = 3, y = 1 

To write my own function for this, I would recommend creating a macro :

 (defmacro my-swap (ab) `(let ((temp ,a)) (setf ,a ,b) (setf ,b temp))) 

However, since Clayton pointed out that this macro will fail if it is applied to a variable named "temp". Therefore, we can use gensym to create a new variable name (guaranteed not to use) and pass this to the second macro, which actually switches the values:

 (defmacro my-swap-impl (ab 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 the version of the previous swap macro that takes a third argument, which will serve as the name of the temporary variable. This is called from a simple macro:

 (defmacro my-swap (ab) ;;simply passes a variable name for use in my-swap-impl `(my-swap-impl ,a ,b ,(gensym))) 

This setting can be used in exactly the same way as the previous one, except that it is safe for capturing variables .

+5
source

First of all, you must make sure that you understand your task correctly. Returning changed inputs does not correspond to changing inputs.

The returned modified inputs are trivial. Consider this simple example:

 (defun foo (bar) (1+ bar)) 

This function will return the input bar , modified by adding 1 to it. You can come up with a more general function that performs the input and the modification procedure and applies it to the input (or inputs). Such a function is called apply :

 CL-USER> (apply '1+ '(1)) 2 

Now, if you want to change the value of the variable passed to the function, this is really impossible to do simply because Lisp uses a pass-by-value rather than a pass-by-reference or pass-by-name for the function. Therefore, this task is usually performed using special or universal modifications, such as setf , that use call-by-name.

However, there is one more work that may be useful in some limited cases - you cannot change the value of a variable, but you can change the value stored in some data structure (since data structures are passed by value, not through a copy ) Therefore, if you pass a data structure to a function, you can change its value inside it. For instance,

 (defun swap (v1 v2) (psetf (elt v1 0) (elt v2 0) (elt v2 0) (elt v1 0))) CL-USER> (defvar *v1* #(0)) CL-USER> (defvar *v2* #(1)) CL-USER> (swap *v1* *v2*) CL-USER> (format t "~A ~A" *v1* *v2*) #(1) #(0) 

But I have to repeat that this approach can be applied only in a limited number of scenarios, when you really know that this is what you need.

+3
source

This is just a comment, not an answer.

The "manually install a, b, c" part may help a little with destructuring-bind.

To make Hanoi’s towers in “state change” mode, I would call a move function, for example (move n stacks 0 2) , which moves n disks from the stack (elt stacks 0) to another stack (elt stacks 2) , thereby avoiding links. "

If you want to call it (move n source target) without writing it as a macro, the source and target must be some kind of encapsulated stack-like objects that you implement from Lisp lists, maybe they have data slots and their own push / pop methods that make their slots point to new memory cells, but do not change the memory location of the stack objects themselves. Like embedding some encapsulated String class from strings with a null C character so that users of the String class don't have to resort to “double access to relations” tricks, such as double pointers (to C) or as double references to Relation: “Name stacks belong to the list , which in turn has a slot that refers to a list .. "(in (move n stacks 0 2) ).

One way to implement the stack (Emacs Lisp only):

 (defun make-hanoi-stack (&rest items) (cons items "unused slot")) (defun hanoi-stack-push (item hanoi-stack) (push item (car hanoi-stack))) (defun hanoi-stack-pop (hanoi-stack) (pop (car hanoi-stack))) (defun hanoi-stack-contents (hanoi-stack) (car hanoi-stack)) 

Using stacks:

 (defun move-one-item (from-hanoi-stack to-hanoi-stack) (hanoi-stack-push (hanoi-stack-pop from-hanoi-stack) to-hanoi-stack)) (let ((stack1 (make-hanoi-stack 1 2 3)) (stack2 (make-hanoi-stack 4))) (move-one-item stack1 stack2) (print (hanoi-stack-contents stack1)) (print (hanoi-stack-contents stack2))) 
0
source

(make sure you understand what makes setf special ). Suppose you want to write a function that modifies the contents of a variable specified as an input. It is usually called destructive or in place. For example, you have a list (setq xs (list 1 2 3)) , you call a function on your list (f xs) , and now your list is (4 5 6) .

 (defun f (xs) (setf xs (list 4 5 6))) ; this won't work (defun f (xs) (setf (car xs) 4) (setf (cdr xs) (list 5 6))) ; but this will (defun f (xs) (setf (elt xs 0) 4)) ; this will change xs to (4 2 3) 

In Common Lisp, changing the value of a local (lexical) variable inside a function will simply bind this local variable to another value. (setf xs '(1 2 3)) just expands to (setq xs '(1 2 3)) . But when you change the internal structure that the binding points to, that change will be visible to every binding that points to it. Therefore, if you change the location of the list with setf , it will destroy the modified input variable.

A common convention in Common Lisp is to use destructive updates as little as possible, usually on a newly created local variable whose final value is not yet visible to the rest of the code. Functional programming discourages destructive updates, so you can only have functions that do not affect its input, but only return new values. Therefore, if you come up with intricate ways to change data in place, then you are doing something wrong, try writing a function that returns new data.

0
source

All Articles