Initialization of variables

Discussion of Common Lisp
Post Reply
justanoob
Posts: 5
Joined: Wed Nov 04, 2009 6:17 am

Initialization of variables

Post by justanoob » Mon Jan 11, 2010 6:49 am

The problem was to take an even numbered list and break it into pairs of two each.
So for list ("a" "b" "c" "d" "e" "f"), the output should be (("a" "b") ("c" "d") ("e" "f")).

Code: Select all

 (progn
        (setf mfiles '("a" "b" "c" "d" "e" "f"))
        (let (ret)
        (dotimes (i (/ (list-length mfiles) 2))
          ;(format t "~&Ret is ~a ~%" ret)           
           (setf ith (nth (* i 2) mfiles))
           (setf i1th (nth (+ (* i 2) 1) mfiles))
           (setf entry (list ith i1th))
           (setf ret (append ret (list entry)))
        )
        ret
        )
      )
Earlier I had written

Code: Select all

(let (ret)
line as

Code: Select all

(setf ret nil)
Then the format t stmt. gave the output as :Ret is 3. Then, it gave an error in the line

Code: Select all

(setf ret (append ret (list entry)))
that '3' is not of type list.
Please explain. Please ignore the mismatch in brackets for the code above.

Thanks....

blandest
Posts: 19
Joined: Mon Jun 30, 2008 1:22 am

Re: Initialization of variables

Post by blandest » Mon Jan 11, 2010 7:13 am

The function #'append expects a list as a first parameter, but you have passed 'ret' which has the value of '3'. I am not sure if your code will work as desired, but you can try this:

Code: Select all

(append (list ret) (list entry))
I would solve your problem with something like this:

Code: Select all

(let ((l (list "a" "b" "c" "d")))
   (loop for x in l by #'cddr
           for y in (rest l) by #'cddr
           collect (list x y)))

justanoob
Posts: 5
Joined: Wed Nov 04, 2009 6:17 am

Re: Initialization of variables

Post by justanoob » Mon Jan 11, 2010 7:19 am

I still have to look in your code......
But my problem is that I had initialized ret as nil with

Code: Select all

(setf ret nil)
Still, it was having a value of 3....

blandest
Posts: 19
Joined: Mon Jun 30, 2008 1:22 am

Re: Initialization of variables

Post by blandest » Mon Jan 11, 2010 8:24 am

I suggest using a debugger for better understanding how your variables get changed. Also, you may want to look for the definition of the "classic" #'group function. That will teach you a generic (beautiful) solution to your problem; your code resembles C too much :)

JamesF
Posts: 98
Joined: Thu Jul 10, 2008 7:14 pm

Re: Initialization of variables

Post by JamesF » Tue Jan 12, 2010 6:50 pm

Let me guess: you're coming to Lisp from C, Perl or Java? :)
There are two giveaways: the indentation of the closing parentheses, and the way you're using variables.

While you can write C in Lisp, you're setting yourself up to fight the language instead of getting it to work with you. This is where we run into probably my favourite analogy describing functional programming: it's like roller-skating. It doesn't work all that well without skates, and they're awkward to walk in (what you're doing with the code above). Getting the hang of it can be hard, and it's easy to get frustrated and go back to walking. When you start skating with those things actually attached to your feet, though, suddenly it all makes sense and you understand what the fuss was about.
Something that it's important to understand is the "function" part of "functional programming" - while C is oriented around creating structures and then poking at them from the outside, Lisp allows you to pass the output of one function directly to another without having to explicitly name it. If there were a single thing that constituted the roller-skates of Lisp, this would be it. This difference goes right to the heart of how C and Lisp approach things, which is a discussion for another post.

The natural question following this, of course, is "OK, smartarse, what is the idiomatic approach?"
That would be to write a function which takes a list as its argument, and which returns a new list composed of lists containing pairs of the elements in the original list. How you handle odd-numbered lists is up to you: either respond with (error "List must have an even number of arguments!"), wrap it somehow as the last element (more on this in a minute) or silently drop it.

In case you're still getting the hang of lists, they're created via the (cons) function. The catch is that it's only a list in Lisp terms if the last element is NIL (a.k.a. the empty list) - that is, (cons "a" nil) is a list, as is (cons "a" "b" nil), while (cons "a" "b") is "merely" a dotted pair. The practical differences that are most likely to be of interest here is what you get when you call (cdr) on the result - it'll give you the last element of a dotted pair, but what you get back from the other constructions is... probably something you want to explore yourself, with a useful reference at hand.
Where I'm going with all this is that you do have to decide what form the "pairs" should take in the output list, which should be determined by what you plan to do with them next. Sometimes you'll want (list "a" "b"); other times you'll want (cons "a" "b").
I really hope that made sense, but do feel free to point out where I just sound like I'm babbling.

What I'd probably do when writing something like this is set up the scenario, then construct the machinery that makes it work. First, the scene:

Code: Select all

(defvar *foo* (list "a" "b" "c" "d" "e" "f"))
(list-into-pairs *foo*)
It's useful to know that (list "a" "b" "c" "d" "e" "f") is very different to '("a" "b" "c" "d" "e" "f") - the former generates a brand new list each time you call it, while the latter refers to an immutable structure in memory. When doing something like this, you almost certainly want to use (list) unless you have a specific reason not to.
It's also handy to know that variables are declared/initialised via (defvar) or (defparameter). (setf) is for updating the value of an existing variable; it's bad form to use it to initialise one. The reason for having two ways of declaring a variable is that (defvar) will only set the value of the variable if it doesn't exist; (defparameter) will set it every time. This relates to the interactive way in which Lisp code is normally written and tested: if you reload a source-file into a running image, you may or may not want the current state of a variable reset to its beginning value.

The next part is writing

Code: Select all

(list-into-pairs)
, which starts like this:

Code: Select all

(defun list-into-pairs (lst)
;iterate over the input list, collecting pairs of elements
)
Why do it like this? One thing that Lisp has really taught me is not just to solve the exact problem at hand, but instead to recognise the entire class of problem it represents, and to solve that whole class at once. My code typically winds up as a library plus an application that happens to call functions from it. Conveniently, both approaches seem to involve about the same amount of work... at least, they do until you need to add things like error-handling. But then when I need to solve that same problem again in a slightly different form, I already have the solution at hand. Also, if all you needed was that exact list of strings grouped in pairs, you'd just create that group of pairs in the first place, wouldn't you? :)

Because this looks remarkably like homework, I won't give you the actual code for turning the input list into a group of pairs, since that would seriously reduce the amount you'd learn from the exercise, and that would defeat the entire point of it. However, I've hopefully given you a bit of help on the way. Incidentally, I'd be inclined to use (loop) with its collecting keyword to implement the approach you've used, but there's always more than one approach.

sthome12
Posts: 1
Joined: Wed Jan 13, 2010 5:51 am

Re: Initialization of variables

Post by sthome12 » Wed Jan 13, 2010 6:09 am

Waaahh..I can barely understand the codes above.
I'm currently learning Lisp, and I haven' tackled a lot yet.
I'm going to let my mentor see the codes above so I'll be able to know
them better and implement them.

Post Reply