New to LISP could use some help...thanks.

Discussion of Common Lisp
Post Reply
black_hat
Posts: 5
Joined: Sun Sep 05, 2010 9:53 am

New to LISP could use some help...thanks.

Post by black_hat » Sun Sep 05, 2010 10:39 am

Hello all,

I am writing a function that modifies an association list. In my code I have the following variable:

Code: Select all

(defvar *products* nil)
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.

audwinc
Posts: 12
Joined: Thu Sep 02, 2010 11:46 am

Re: New to LISP could use some help...thanks.

Post by audwinc » Sun Sep 05, 2010 7:56 pm

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.

jiptohej
Posts: 1
Joined: Mon Sep 06, 2010 5:36 am

Re: New to LISP could use some help...thanks.

Post by jiptohej » Mon Sep 06, 2010 6:04 am

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

audwinc
Posts: 12
Joined: Thu Sep 02, 2010 11:46 am

Re: New to LISP could use some help...thanks.

Post by audwinc » Mon Sep 06, 2010 9:04 am

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.

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: New to LISP could use some help...thanks.

Post by ramarren » Mon Sep 06, 2010 9:28 am

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.

black_hat
Posts: 5
Joined: Sun Sep 05, 2010 9:53 am

Re: New to LISP could use some help...thanks.

Post by black_hat » Wed Sep 08, 2010 9:31 am

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.

Post Reply