&optional parameter in lisp macros: Why does this variable behave like this? - macros

I'm trying to create a lisp macro that has an &optional parameter with a default value. Unfortunately the parameter is processed differently depending on whether it is read from the default value or from parameters provided to the macro. The code fragment below reproduces the issue:
(setf table1 '((1 2 3)
(4 5 6))
table2 '((10 20 30)
(40 50 60)))
(defmacro test-lambda (f &optional (tableau table1))
`(list ,f ,tableau))
? (test-lambda 0 table2) ;; This works...
(0 ((10 20 30) (40 50 60)))
? (test-lambda 0) ;; ...but this doesn't
> Error: Car of ((1 2 3) (4 5 6)) is not a function name or lambda-expression.
> While executing: CCL::CHEAP-EVAL-IN-ENVIRONMENT, in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.
1 >
I don't quite understand why the macro won't work with the default value in the second case. Is there a better way to code this or at least a workaround?
Thanks,

What
You need to quote the default argument value:
(defmacro test-lambda-1 (f &optional (tableau 'table1))
`(list ,f ,tableau))
(test-lambda-1 0)
==> (0 ((1 2 3) (4 5 6)))
Why
You need to think how Common Lisp evaluates your code: when it sees (test-lambda ...), it
expands the macro
evaluates the resulting code
Let us try it:
(macroexpand '(test-lambda 0))
==> (LIST 0 ((1 2 3) (4 5 6))) ; T
(macroexpand '(test-lambda-1 0))
==> (LIST 0 TABLE1) ; T
(macroexpand '(test-lambda 0 table2))
==> (LIST 0 TABLE2) ; T
(macroexpand '(test-lambda-1 0 table2))
==> (LIST 0 TABLE2) ; T
Now you can see where the error is coming from: you did not quote the default value of the argument, so it got evaluated twice.

Related

Introducing new symbols into Common-Lisp Function/Macro

I want to define a functionality in common lisp which uses some words/symbols not known to lisp, for example having such a function/macro:
(my-func-or-macro lst key-word idx)
which returns items in the lst upto the idx if the key-word is to and from the idx to the end if the key-word is from.
So practically it should work like:
(my-func-or-macro '(1 2 3 4 5 6) from 3) => '(4 5 6)
and
(my-func-or-macro '(1 2 3 4 5 6) to 3) => '(1 2 3)
Since macros don't evaluate their arguments i think i should be using a macro which doesn't care about from and to (note i don't want to pass quoted args like 'from 'to).
Since every argument to a function is evaluated before the function is executed, you need a symbol that evaluates to itself.
Such symbols are already part of the language: all symbols in the special KEYWORD package evaluate to themselves. Symbols that are written with a leading colon, e. g. :foo are interned into that package.
Also part of the language are keyword arguments that use such keywords to identify parameters:
(defun my-func (list &key from)
(subseq list from))
(my-func '(0 1 2 3 4 5) :from 3)
=> (3 4 5)
You can give default values to such parameters:
(defun my-func (list &key (from 0) (to (length list)))
(subseq list from to))
(my-func '(0 1 2 3 4 5) :from 3)
=> (3 4 5)
(my-func '(0 1 2 3 4 5) :to 3)
=> (0 1 2)
(my-func '(0 1 2 3 4 5) :from 1 :to 4)
=> (1 2 3)
Keyword arguments can be given in any order:
(my-func '(0 1 2 3 4 5) :to 4 :from 1)
=> (1 2 3)

Macro Issues: Eval of a macro body works, but the macro doesn't

Consider the following code snippet:
[1]> (defvar *clist* '((2 1 21) ( 3 2 32) (4 3 43)))
*CLIST*
[2]> (eval `(case '1 ,#(mapcar #'rest *clist*)))
21
[3]> (defmacro tester (index clist)
`(case ,index ,#(mapcar #'rest clist)))
TESTER
[4]> (tester '1 *clist*)
*** - MAPCAR: A proper list must not end with *CLIST*
The following restarts are available:
ABORT :R1 Abort main loop
Break 1 [5]>
The code contains the error-message generated.
As one can clearly see, that eval of the code that is used as the body
of the macro tester , gives a result. But the same code (by replacing
*clist* and '1, by clist and index variables.) doesn't work when it's used as a body of the macro.
When testing backquote, just print it:
> `(case '1 ,#(mapcar #'rest *clist*))
(CASE '1 (1 21) (2 32) (3 43))
When testing macros, you do not evaluate them (either at REPL or, all
the more so, using eval
explicitly).
You expand the macro using macroexpand
and examine the code.
E.g.,
> (macroexpand-1 '(tester '1 *clist*))
*** - MAPCAR: A proper list must not end with *CLIST*
This tells you that tester passes the symbol *CLIST* instead of
its value to mapcar.
You need to think what you are trying to do in terms of
"compile-time"
vs "execution time".
Do you know the index at compile time?
Do you know the clist at compile time?
In your case, there is no reason to use case:
(defmacro tester (index clist) `(third (find ,index ,clist :key #'second)))
(macroexpand-1 '(tester 1 *clist*))
==> (THIRD (FIND 1 *CLIST* :KEY #'SECOND)) ; T
(tester 1 *clist*)
==> 21
Since you do not know the value of clist at compile time (only the
variable name where it is stored), there is
no win in using case - it has to know all clauses at compile time.
Let's take your macro, remove the expansion and just print its arguments:
CL-USER 4 > (defmacro tester (index clist)
(print (list :macro-tester :index index :clist clist))
nil)
TESTER
Now we call it with your example:
CL-USER 5 > (tester 1 *clist*)
(:MACRO-TESTER :INDEX 1 :CLIST *CLIST*)
NIL
So index is 1 and clist is the symbol *clist*.
Now let's try to call mapcar with the function rest and the symbol *clist*:
CL-USER 9 > (mapcar #'rest '*clist*)
Error: *CLIST* (of type SYMBOL) is not of type LIST.
1 (abort) Return to level 0.
2 Return to top loop level 0.
That's the error you are seeing. The symbol *clist* is not a valid list.

How can I write a MEMBER function using the DO macro in Common Lisp?

I am trying to make a function which works like MEMBER function in Common Lisp.
So, I want to make this function work like this :
(my-member 2 '(1 4 5 5 3 2 5 6 9))
=> (2 5 6 9)
This is exactly same as how MEMBER function works.;
(member 2 '(1 4 5 5 3 2 5 6 9))
=> (2 5 6 9)
The condition is that I should use 'DO' macro to make this function.
This is my code that I have written to make this function :
(defun my-member (item x)
"This function works like MEMBER function."
(do ((z x (rest z))
(e (first x) (first z)))
(:when (equal item (first z))
(return z))))
But it doesn't work..
(my-member 2 '(3 4 5 2 1 1))
-> (3 4 5 2 1 1)
What should I do to solve this problem?
Here is the correct way to use do:
(do ((var 0 (1+ var))
(lst '() (cons var lst)))
((= var 5) lst)))
; ==> (4 3 2 1 0)
So both var and lst are variables initialised to 0 and () and after each iteration the variable is set to (1+ var) and (cons var lst).
What determines when it should stop is (= var 5) becoming not nil and when that happens the result of the whole do form is lst. This is the second part of the do and the last this has since I do not supply a body.
You can make an equivalent of the member function with using only one variable and a second part with an end condition and what should be the result of the do. Good luck!

DELETE + SETF inside a function

I'm trying to write a function that will destructively remove N elements from a list and return them. The code I came up with (see below) looks fine, except the SETF is not working the way I intended.
(defun pick (n from)
"Deletes (destructively) n random items from FROM list and returns them"
(loop with removed = nil
for i below (min n (length from)) do
(let ((to-delete (alexandria:random-elt from)))
(setf from (delete to-delete from :count 1 :test #'equal)
removed (nconc removed (list to-delete))))
finally (return removed)))
For most cases, this works just fine:
CL-USER> (defparameter foo (loop for i below 10 collect i))
CL-USER> (pick 3 foo)
(1 3 6)
CL-USER> foo
(0 2 4 5 7 8 9)
CL-USER> (pick 3 foo)
(8 7 0)
CL-USER> foo
(0 2 4 5 9)
As you can see, PICK works just fine (on SBCL) unless the element being picked happens to be the first on the list. In that case, it doesn't get deleted. That's because the only reassignment happening is the one that goes on inside DELETE. The SETF doesn't work properly (i.e. if I use REMOVE instead, FOO does not change at all).
Is there any scoping rule going on that I'm not aware of?
A (proper) list consists of cons cells that each hold a reference to the next
cell. So, it is actually a chain of references, and your variable has a
reference to the first cell. To make this clear, I rename the binding outside
of your function to var:
var ---> [a|]--->[b|]--->[c|nil]
When you pass the value of the variable to your function, the parameter gets
bound to the same reference.
var ---> [a|]--->[b|]--->[c|nil]
/
from --'
You can update the references in the chain, for example eliminate b:
var ---> [a|]--->[c|nil]
/
from --'
This has an effect on the list that var sees outside.
If you change the first reference, for example eliminating a, this is just the
one originating from from:
var ---> [a|]--->[b|]--->[c|nil]
/
from --'
This has obviously no effect on what var sees.
You need to actually update the variable binding in question. You can do that
by setting it to a value returned by function. Since you already return a
different value, this would then be an additional return value.
(defun pick (n list)
(;; … separate picked and rest, then
(values picked rest)))
Which you then use like this, for example:
(let ((var (list 1 2 3)))
(multiple-value-bind (picked rest) (pick 2 var)
(setf var rest)
(do-something-with picked var)))
Now to the separation: unless the list is prohibitively long, I'd stick to
non-destructive operations. I also would not use random-elt, because it needs
to traverse O(m) elements each time (m being the size of the list),
resulting in a runtime of O(n·m).
You can get O(m) overall runtime by determining the current chance of picking
the current item while linearly running over the list. You then collect the
item into either the picked or rest list.
(defun pick (n list)
(loop :for e :in list
:and l :downfrom (length list)
:when (or (zerop n)
(>= (random 1.0) (/ n l)))
:collect e :into rest
:else
:collect e :into picked
:and :do (decf n)
:finally (return (values picked rest))))
Delete isn't required to modify any structure, it's just permitted to. In fact, you can't always do a destructive delete. If you wanted to delete 42 from (42), you'd need to return the empty list (), which is the symbol NIL, but there's no way that you can turn the list (42), which is a cons cell (42 . NIL) into a different type of object (the symbol NIL). As such, you'll probably need to return both the updated list, as well as the elements that were removed. You can do that with something like this, which returns multiple values:
(defun pick (n from)
(do ((elements '()))
((or (endp from) (zerop n))
(values elements from))
(let ((element (alexandria:random-elt from)))
(setf from (delete element from)
elements (list* element elements))
(decf n))))
CL-USER> (pick 3 (list 1 2 3 2 3 4 4 5 6))
(2 6 4)
(1 3 3 5)
CL-USER> (pick 3 (list 1 2 3 4 5 6 7))
(2 7 5)
(1 3 4 6)
CL-USER> (pick 2 (list 1 2 3))
(2 3)
(1)
CL-USER> (pick 2 (list 1))
(1)
NIL
On the receiving end, you'll want to use something like multiple-value-bind or multiple-value-setq:
(let ((from (list 1 2 3 4 5 6 7)))
(multiple-value-bind (removed from)
(pick 2 from)
(format t "removed: ~a, from: ~a" removed from)))
; removed: (7 4), from: (1 2 3 5 6)
(let ((from (list 1 2 3 4 5 6 7))
(removed '()))
(multiple-value-setq (removed from) (pick 2 from))
(format t "removed: ~a, from: ~a" removed from))
; removed: (3 5), from: (1 2 4 6 7)
delete does not necessarily modify its sequence argument. As the hyperspec says:
delete, delete-if, and delete-if-not return a sequence of the same type as sequence that has the same elements except that those in the subsequence bounded by start and end and satisfying the test have been deleted. Sequence may be destroyed and used to construct the result; however, the result might or might not be identical to sequence.
For instance, in SBCL:
* (defvar f (loop for i below 10 collect i))
F
* (defvar g (delete 0 f :count 1 :test #'equal))
G
* g
(1 2 3 4 5 6 7 8 9)
* f
(0 1 2 3 4 5 6 7 8 9)
*
Note that in your function setf modifies the local variable from, and since delete in the case of first element does not modify the original list, at the end of the function the variable foo maintains the old values.

Lisp Recreating a Temporary Variable

I'm having a bit of trouble with Lisp. What i'm attempting to do, is keep track of the amount of times a number appears in x number of lists. However, running this over and over again, lisp isn't recreating the variable, but using the ending value from the last time I called the function. So I'm wondering how can I get past the 'binding' powers of let?
So, I've got some list like this
(((8 7) (3)) ((8 3) (2)) ((7 3) (6)) ((7 2) (8)) ((6 7) (4 1))
((6 6) (4 1)) ((6 2) (2)) ((5 6) (3)) ((5 3) (8 3)) ((4 6) (4))
((4 4) (6)) ((4 1) (7)) ((3 7) (5 3)) ((3 4) (1)) ((3 3) (3)) ((3 1) (9))
((2 7) (7)) ((2 5) (2)) ((2 2) (5 2)) ((1 7) (1)) ((1 6) (6 1))
((1 1) (2 1)) ((1 0) (3)) ((0 7) (8 1)) ((0 5) (6)) ((0 3) (9 6))
((0 1) (1)))
Then I'm calling some function like this, (declaring var here doesn't seem to do anything, past the initial function call)... I guess some kind of binding from let.
(defun counter (possibleValues)
(let ((var '(0 0 0 0 0 0 0 0 0 0)))
(loop for i from 0 to (list-length possibleValues) do
(loop for j in (cdr (nth i possibleValues)) do
(loop for k in j do
(incf (nth k var)))))
var))
So I can run my list through the function and get something like
(0 8 5 6 3 2 5 2 3 2)
Each position referring to the number found in the list. So the value 8 would refer to how many times 1 was found in all the lists (i'm considering the second list only). Now the problem.... run it twice and...
(0 16 10 12 6 4 10 4 6 4)
I was using an associative list earlier, but in trying to figure this out and keep things simple, i'm now using a list. I guess another question I have is, how can I create associative list elements on the fly? I don't like declaring 'var' like that, but I'm just trying to get around 'let' for the moment. I haven't had much luck with 'setq' or 'setf' either....
Thanks in advance for your help!
Change the initialization form for VAR to be an expression that creates new lists, such as (make-list 10 :initial-element 0) or even (list 0 0 0 0 0 0 0 0 0 0).
Basically, do not ever use quoted objects if you have intentions on modifying them, as the consequences are undefined if you do. In fact, evaluating that function definition gives a warning about that:
; in: LAMBDA NIL
; (INCF (NTH K VAR))
; --> LET*
; ==>
; (SB-KERNEL:%SETNTH #:TMP5 #:TMP4 #:NEW3)
;
; caught WARNING:
; Destructive function SB-KERNEL:%SETNTH called on constant data.
; See also:
; The ANSI Standard, Special Operator QUOTE
; The ANSI Standard, Section 3.2.2.3
;
; compilation unit finished
; caught 1 WARNING condition