Beginners Question - Can't manipulate a local variable

Discussion of Common Lisp
webguy08
Posts: 14
Joined: Sat Jan 02, 2010 4:11 pm

Beginners Question - Can't manipulate a local variable

Post by webguy08 » Sat Jan 02, 2010 4:20 pm

Hi all,

I'm having a problem trying to get a local-variable in my Lisp code to be manipulated. I have no idea why it refuses to change!
Here is the code:

Code: Select all

(setf list ((title "New Super Mario Bros. Wii")
	(genre "Platform")
	(platform "Wii")))

(defun rate (&optional title genre platform)
	(dolist (element list) ; element is the specific game. In this case, New Super Mario Bros. Wii.
		(setf element (append element '((rating 0))))
		(let ((game-rating 0))
			(dolist (e element) ; e will be each attibute in the element, such as (genre "Platform").
				(cond ((equal (second e) title) (+ game-rating 1))
					((equal (second e) genre) (+ game-rating 1))
					((equal (second e) platform) (+ game-rating 1))))
			(setf (second (assoc 'rating elem)) game-rating)
			(print element))))
game-rating does not seem to increment for some reason.

Any help will be appreciated.

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

Re: Beginners Question - Can't manipulate a local variable

Post by ramarren » Sun Jan 03, 2010 3:38 am

webguy08 wrote:

Code: Select all

(setf list ((title "New Super Mario Bros. Wii")
   (genre "Platform")
   (platform "Wii")))
This doesn't even work, since there is an unquoted non-valid form there, but even assuming that there it was quoted this is invalid behaviour, since you SETF is used to set places, not create variables. Most implementations will create a global variable, but this is not standard. Use DEFPARAMETER to create global variables. Also, it is an usual convention to surround the name of global variables with 'earmuffs', that is, stars, '*list*', to make it obvious that their binding is dynamic.

In any case, one of your problems here is that you can, in fact, manipulate a local variable, and you do. And then, since it is local, in immediately goes out of scope and you lose results of your manipulation.

Like in here:

Code: Select all

(setf element (append element '((rating 0))))
you just change the local iteration variable, without touching anything else.

Also, what do you expect:

Code: Select all

(dolist (e element) ; e will be each attibute in the element, such as (genre "Platform").
        (cond ((equal (second e) title) (+ game-rating 1))
              ((equal (second e) genre) (+ game-rating 1))
              ((equal (second e) platform) (+ game-rating 1))))
to do? Function '+' computes and returns a sum of two numbers, it doesn't change anything. Not to mention that it doesn't really make sense for other reasons, what if the title is the same as the platform?

In general, it is best to treat lists in Common Lisp as functional datastructures, since while they can be mutated, it doesn't always do what you expect, as there are no actual lists in Common Lisp as a complete ADT. If you want to use mutable records, I would suggest using classes. Or at the very least a hash table.

webguy08
Posts: 14
Joined: Sat Jan 02, 2010 4:11 pm

Re: Beginners Question - Can't manipulate a local variable

Post by webguy08 » Sun Jan 03, 2010 7:23 am

Aaahh! I feel so stupid :lol:. I didn't ever change game-rating, I just returned its value.
Sorry about the missing quote mark. I shortened the list do save space and must have accidentally deleted it.

Thanks for the help!

webguy08
Posts: 14
Joined: Sat Jan 02, 2010 4:11 pm

Re: Beginners Question - Can't manipulate a local variable

Post by webguy08 » Mon Jan 04, 2010 11:28 am

I don't want to start a new thread for the following question because it's relatively small.

Using the following list:

Code: Select all

(setf game-list '(((title "New Super Mario Bros. Wii") (genre "Platform") (platform "Wii") (price 20))
                        ((title "Quake") (genre "First Person Shooter") (platform "PC") (price 5)))
If I wanted to sort that list according to price, how on Earth would I go about doing so?
I have tried looking into it, and have seen examples like so:

Code: Select all

(setf l (1 4 5 6 3))
(sort l #'>)
Or something like that, but that's is a fairly simple list to sort. Here is what I have tried, but it doesn't work:

Code: Select all

(sort game-list #'(second (fourth (first game-list))))
Any ideas?

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

Re: Beginners Question - Can't manipulate a local variable

Post by ramarren » Mon Jan 04, 2010 11:46 am

See the documentation of SORT, in particular the key argument. There are even examples similar to what you want, although you probably would need a more complex anonymous function. It would be easier if the data was represented as classes, as the key would just be an accessor, rather than a partially applied assoc call.

Also note that SORT is destructive, in the sense that it destroys the sorted list, reusing its storage, and returning the sorted list. Any references to the old list will point most likely to some fragment. This also mean that you should not sort a literal list (created by QUOTE), as it is in principle read-only and the consequences are undefined and usually bad.

What are you learning Common Lisp from? This is, as are many other things, well explained in Practical Common Lisp, which is the text I would recommend. There are quite many Common Lisp tutorials on the web which are either archaic or just bad.

webguy08
Posts: 14
Joined: Sat Jan 02, 2010 4:11 pm

Re: Beginners Question - Can't manipulate a local variable

Post by webguy08 » Mon Jan 04, 2010 1:16 pm

I'm just using various sources on the internet. Thanks for those 2 you have given me! I've bookmarked them, they seem to provide a lot of detail about everything.
I'll have a look into sorting; perhaps just write my own sorting method. I'm thinking of putting everything in an array and then sorting it in there since I find arrays easy to work with, and then regurgitating it.

Just a final question about Lisp :P. If you want to check several arguments do you have to use lots of 'if' statements, or is there a tidier alternative? I have used cond on several occasions, but cond doesn't execute all of the conditions, only the first one which returns true. I need something that checks each argument, and if it's true then execute it.

Thanks for the help.

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

Re: Beginners Question - Can't manipulate a local variable

Post by ramarren » Mon Jan 04, 2010 1:38 pm

webguy08 wrote:I'll have a look into sorting; perhaps just write my own sorting method. I'm thinking of putting everything in an array and then sorting it in there since I find arrays easy to work with, and then regurgitating it.
The standard sort function is perfectly fine (and it does work on vectors in place) as long as you remember those things I mentioned, and it is unlikely that you would be able to write anything better. For one thing, since it is a standard function the compiler is allowed to optimize it using type inference information.
webguy08 wrote:Just a final question about Lisp :P. If you want to check several arguments do you have to use lots of 'if' statements, or is there a tidier alternative? I have used cond on several occasions, but cond doesn't execute all of the conditions, only the first one which returns true.
That rather depends on what exactly you want to do. Usually if there is some code pattern then it can be extracted into a function or a macro. Remember that symbols and functions in particular are first class objects, and can be put in lists and so on.

Also note that there are WHEN and UNLESS macros for when there is no second IF branch.
webguy08 wrote:I need something that checks each argument, and if it's true then execute it.
There is any number of ways to achieve that, depending on what exactly you want. This usually indicates that your arguments should be a number of arguments, but, a possible '&rest', single parameter, to be iterated upon.

webguy08
Posts: 14
Joined: Sat Jan 02, 2010 4:11 pm

Re: Beginners Question - Can't manipulate a local variable

Post by webguy08 » Mon Jan 04, 2010 3:27 pm

I said that my previous post was the last :lol:, but I seem to have come across something new which I can't explain.

Taking the list that I provided in the previous post:

Code: Select all

(setf game-list '(((title "New Super Mario Bros. Wii") (genre "Platform") (platform "Wii") (price 20))
                      ((title "Quake") (genre "First Person Shooter") (platform "PC") (price 5)))
If I create a function like so:

Code: Select all

(defun add-to-list () (setf game-list (append game-list (list (list (list title "Doom") (genre "First Person Shooter") (platform "PC") (price 5))))))
And I write (add-to-list) this would change the list, thus when I call game-list in Lisp it will return the list with that new game. If however I write a function like so:

Code: Select all

(defun add-to-list (l) (setf l (append l (list (list (list title "Doom") (genre "First Person Shooter") (platform "PC") (price 5))))))
And I write (add-to-list game-list) in Lisp, it will not actually change the list. If I call it, it will not have the new game.

I don't understand why this is. Any idea?

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

Re: Beginners Question - Can't manipulate a local variable

Post by ramarren » Mon Jan 04, 2010 3:39 pm

I did explain this in the first reply. In the first case, you use a non-standard automatic global variable (double bad style), in the second you create a new list with appended content and assign it to a local variable, which immediately goes out of scope.

In neither case you 'change the list', since APPEND is explicitly non-destructive, and even if it were (like NCONC) it is a bad idea to use destructive functions for side effects. You just assign a new value to a variable.

webguy08
Posts: 14
Joined: Sat Jan 02, 2010 4:11 pm

Re: Beginners Question - Can't manipulate a local variable

Post by webguy08 » Mon Jan 04, 2010 5:39 pm

I tried changing the list's name to *game-list* but that didn't change the results, so I take it this is just common practice? So, at the moment Lisp thinks that I'm passing in a list and it should treat it as a local variable? How would I make it understand that I want it to change the global variable? Do I have to use defvar, or defparameter? :|

After coming from object-oriented languages this sure is very different :)

Post Reply