views:

164

answers:

5

Hi All,

Below is my code which takes a car element of a list(carVal) and an list(initialized to empty) as parameters. I want to append the element to the list but the same is not working.

(define populateValues
   (lambda (carVal currVal)
      (append currVal(list carVal ))
       (display currVal)))

The display shows empty list all the time () . Can anyone help me understand why?

+1  A: 

(append foo bar) returns the concatenation of foo and bar. It doesn't change either foo or bar.

sepp2k
A: 

You have to update the value of currVal with set!. Your example should have

(set! currVal (append currVal (list carVal))
(display currVal)
Keith Morgan
Note that this will change `currVal` inside the function, but will have no visible effect outside.
Eli Barzilay
+4  A: 
  1. append creates a new list, it does not modify an existing one.
  2. This is because in general, Scheme (and Racket in this case) is a language that prefers functional style.
  3. You could get somewhat closer with a set! -- but even that will disappoint you since it will modify only the local binding.
  4. Note that in Racket in particular, lists are immutable, so there's nothing that can change a list.
  5. Furthermore, even if you could modify a list this way, it's a very inefficient way to accumulate long lists, since you have to repeatedly scan the whole list.
  6. Finally, if you have issues at this level, then I strongly recommend going over HtDP
Eli Barzilay
I need to come up with this functionality. What would you suggest in this case?
darkie15
You can use a `box`, which is a kind of a pointer to a (mutable) value. **BUT** I doubt that you really need this functionality -- newbies often think they must have that because they're used to mutation being the only way to do things.
Eli Barzilay
+3  A: 

Well, there is append! as a primitive, which solves most of your problems, as noted already, Scheme tends to frown on mutation, it is possible, but typically avoided, so all procedures that mutate have a ! (called a bang) at their end.

Also, set! does not mutate data, it changes an environment, it makes a variable point to another thing, the original data is left unchanged.

Mutating data in Scheme is quite cumbersome, but, to give you my own implementation of append! to see how it is done:

(define (append! lst . lsts)
  (if (not (null? lsts))
      (if (null? (cdr lst))
          (begin
            (set-cdr! lst (car lsts))
            (apply append! (car lsts) (cdr lsts)))

          (apply append! (cdr lst) lsts))))

Note the use of set-cdr!, which is a true mutator, it only works on pairs, it mutates data in memory, unlike `set!'. If a pair is passed to a function and mutated with set-cdr! or set-car!, it is mutated every-where in the program.

This obeys the SRFI append! spec which says that it should be variadic and that it should return an undefined value, for instance.

(define l1 (list 1 2 3 4))

(define l2 (list 2 3 4))

(define l3 (list 3 1))

(append! l1 l2 l3)

l1

l2

l3

Which displays:

(1 2 3 4 2 3 4 3 1)
(2 3 4 3 1)
(3 1)

As visible, append! can take an infinite number of arguments and it mutates them all but the last.

Scheme might not be the ideal language for you though. The use of append! as said before is nonstandard, instead, append is preferred, which does not mutate and is called for its return value. Which I implement as such:

(define (append . lsts)
  (cond
    ((null? lsts) '())
    ((null? (car lsts)) (apply append (cdr lsts)))
    (else (cons (caar lsts) (apply append (cdar lsts) (cdr lsts))))))


> (append (list 1 2 3) (list 4 5 6) (list 'granny 'porn))
(1 2 3 4 5 6 granny porn)

Which shows a more familiar Scheme style in the absence of mutation, heavy use of recursion and no use of sequencing.

Edit: If you just want to add some elements to a list and not per se join two though:

(define (extend l . xs)
  (if (null? l) 
      xs
      (cons (car l) (apply extend (cdr l) xs))))

(define (extend! l . xs)
  (if (null? (cdr l))
      (set-cdr! l xs)
      (apply extend! (cdr l) xs)))

(extend '(0 1 2 3) 4 5 6)

(define list1 '(0 1 2 3))

(extend! list1 4 5 6)

list1

Which does what you expect

Lajla
Thanks for the answer .. Btw .. `granny` , `porn` .. You might wanna change them .. Else you might get down voted :)
darkie15
@darkie15 It doesn't make the answer less 'useful' or 'clear', if people want to downvote it because of those things, then this site is already lost. Besides, you've got your answer anyway. =)Besides, other people can edit it out if they want to.
Lajla
A: 

You really need to think about what exact functionality you are looking for

If you want to mutate a referenced list in place, then you have to do the equivalent of append! (as noted in the other answers). But that is dangerous, BECAUSE you may have other code that is counting on the list being immutable, and if you are going to do that, your procedure needs to have a ! in the end to flag that danger.

A cheap approximation to what you want to do, in a more functional style, is:

(define (populateValues carVal currVal)
 (let ((ll (append currVal (list carVal))))
   (display ll)
   ll))

Note that it makes a new list, does the append, displays the result, and RETURNS the new list as a value. This is a useful debugging technique if you don't have access to the intermediate value: bind to a varible, display or log it, and then return it.

Dak