Page 1 of 1
New to LISP could use some help...thanks.
Posted: Sun Sep 05, 2010 10:39 am
by black_hat
Hello all,
I am writing a function that modifies an association list. In my code I have the following variable:
and the following function:
Code: Select all
(defun add-product (alst widget quantity)
(if (assoc widget alst) (setf (cdr (assoc widget alst)) (+ quantity (cdr (assoc widget alst)))) ;if key present update quantity
(setf alst (acons widget quantity alst)))) ;else add (key . data) to alst
When I run this from the terminal I get the following behavior:
Code: Select all
>(setf *products* '((hat . 3) (scarf . 4)))
>((hat . 3) (scarf . 4))
>(add-product *products* 'hat 5)
>((hat . 8) (scarf . 4))
>*products*
>((hat . 8) (scarf . 4))
>(add-product *products* 'boots 10)
>((boots . 10) (hat . 8) (scarf . 4))
>*products*
>((hat . 8) (scarf . 3))
I can't seem to figure out why one action will modify the global variable *products* whereas the other will not.
Thanks for the help.
Re: New to LISP could use some help...thanks.
Posted: Sun Sep 05, 2010 7:56 pm
by audwinc
You are modifying the local variable alst with your setf, which effectively rebinds alst to a totally new value but does not rebind the thing you passed in. The reason the other setf works is because you are modifying elements alst is pointing to.
Re: New to LISP could use some help...thanks.
Posted: Mon Sep 06, 2010 6:04 am
by jiptohej
defun add-product (alst widget quantity)
(if (assoc widget *products* (setf (cdr (assoc widget *products*))
(+ quantity (cdr (assoc widget *products*)))) ;if key present update quantity
(setf *products*(acons widget quantity *products*)))) ;else add (key . data) to alst
;;; Greets
Re: New to LISP could use some help...thanks.
Posted: Mon Sep 06, 2010 9:04 am
by audwinc
jiptohej:
That works, but it makes alst useless and forces use of a global variable, which may have just been used here to help the example. How would you solve the problem if he did it like this?
Code: Select all
(let ((products nil))
(add-product products "boot" 3)
(add-product products "umbrella" 1)
(add-product products "boot" 2)
(print products))
Spoilers: For fun, I worked this out on my own, and the only viable solution I could come up with was doing a setf on the cdr of the end of alst. Other solutions involve stranger ways to pass the variable into the function (like passing it as the only member of a cons) or using a macro. Finally, I favor a functional approach of just returning the modified version of the alist, but if he intends to make this a large database of widgets, he might use a hash instead, which can be easily modified in-place.
Re: New to LISP could use some help...thanks.
Posted: Mon Sep 06, 2010 9:28 am
by ramarren
audwinc wrote:(like passing it as the only member of a cons)
I think the most hilarious (well, if you find programming tricks funny) way to accomplish this sort of thing is to use zero-dimensional arrays.
Code: Select all
(aref (make-array nil :initial-element 'a-thing))
Doing that is almost never makes sense, because normally you would just properly reify the datastructure, but it is an interesting bit in the specification.
Re: New to LISP could use some help...thanks.
Posted: Wed Sep 08, 2010 9:31 am
by black_hat
Thank you for all the replies, they helped me to understand this. For the "alternate solutions", this is part of a larger assignment and we need to use association lists.
Thanks Again.