Scheme: change value of an element in a list

15,620

Solution 1

You can write list-set! of Guile, like so:

(define a (list 1 2 3 4))     ; a is '(1 2 3 4)

(define (list-set! list k val)
    (if (zero? k)
        (set-car! list val)
        (list-set! (cdr list) (- k 1) val)))

(list-set! a 2 100)           ; a is '(1 2 100 4)

(Tried this in DrRacket.)

Solution 2

I may be a bit late, but I have a different answer.

Part of the functional-program paradigm seems to be to try to avoid modifying data when possible. For efficiency reasons you may want to go with the other answers here. But otherwise, consider a non-mutating function such as this:

(define (list-with lst idx val)
  (if (null? lst)
    lst
    (cons
      (if (zero? idx)
        val
        (car lst))
      (list-with (cdr lst) (- idx 1) val))))

Which passes the following tests:

(describe "a function that returns a list with a 'changed' value"
  (it "can modify the edges of lists without having 1-off errors"
    (expect (list-with '(1 2 3 4 5) 0 99) (be equal? '(99 2 3 4 5)))
    (expect (list-with '(1 2 3 4 5) 4 99) (be equal? '(1 2 3 4 99))))
  (it "has something to do with creating new lists"
    (expect (list-with '(1 2 3 4 5) 2 99) (be equal? '(1 2 99 4 5))))
  (it "doesnt just modify the contents of the original list"
    (let ((a '(1 2 3 4 5)))
      (list-with a 2 99)
      (expect a (be equal? '(1 2 3 4 5))))))

(The code is written in Chicken Scheme and the tests with the "missbehave" library. But it seems like pretty portable Scheme.)

Solution 3

Using standard functions without any SRFI:

(set-car! (list-tail lst k) val)

Solution 4

Guile has a built-in function called list-set! that does exactly what you want, using zero-based indices. For your example, you would have:

(define a '(1 2 3 4 5))
(list-set! a 3 100)

I don't think this is standard Scheme, however, and I don't know if it's really efficient. For a fixed-length array you should probably use a vector:

(define a2 #(1 2 3 4 5))
(vector-set! a2 3 100)

I'm pretty sure this is part of the language standard.

Share:
15,620
amindfv
Author by

amindfv

Updated on June 04, 2022

Comments

  • amindfv
    amindfv almost 2 years

    I hate using SO as a way to find simple functions, but I really can't find a function like this anywhere:

    Given a list (1 2 3 4 5), I'd like the equivalent of (PHP's, Perl's, Python's)

    $a = array(1, 2, 3, 4, 5);   
    $a[3] = 100;  
    

    Which results in (1 2 3 100 5)

    Thanks!

  • C. K. Young
    C. K. Young over 12 years
    Normally, set! in Scheme doesn't support places. But with SRFI 17 loaded (if your implementation supports it---Guile does), you can use (set! (list-ref lst 3) 100) if you want. EDIT: Oops, that doesn't work in Guile, but (set! (caddr lst) 100) does.
  • amindfv
    amindfv over 12 years
    I think the Scheme equivalent of nth is list-ref, which unfortunately returns a value not a reference (at least in my environment)
  • Eli Barzilay
    Eli Barzilay over 12 years
    Note thaht "returns a reference" is something that doesn't exist in either Scheme or Lisp. The way that CL's setf works is at the macro level, it doesn't work on some "returned reference". Same goes for srfi-17 in Scheme: it doesn't use a "returned reference", instead, it uses the set!-ed function to find a setter function.
  • Keith Layne
    Keith Layne over 12 years
    @amindfv Yeah, I found list-ref but didn't include it in my edit because of exactly what you said.
  • Keith Layne
    Keith Layne over 12 years
    @Eli You be careful, mister, or I'll take away the upvote for your answer to my only question ever on SO. Seriously, you can tell that I don't get to do much Lisp any more...thank you for correcting me. Heck, I'm lucky to get to do any reading or programming these days...
  • Eli Barzilay
    Eli Barzilay over 12 years
    :) No problems... I'm just pointing it out because my experience with these things is that many people do expect there to be some kind of a reference value, then they get surprised when they realize there isn't, and often, that continues with suggestions on how to implement such values after all...
  • hpaulj
    hpaulj almost 7 years
    I was using list-set! in guile. This let me port the script to chicken.