views:

3323

answers:

8

I have a list of things (I'll call it L), an index(N) and a new thing(T). If I want to replace the thing in L at N with T, what is the best way to do this? Should I get the sublist up to N and from N to the end of the list and then glue together a new list from the first part, T, and the last part using list? Or is there a better way to do this?

+1  A: 

Sounds like you want either rplaca or replace. See http://www.lispworks.com/documentation/HyperSpec/Body/f_rplaca.htm or http://www.lispworks.com/documentation/HyperSpec/Body/f_replac.htm#replace

David Plumpton
Probably too much trouble. (setf (nth N L) T), as l0st3d suggested, does the right thing with the minimum of code.
Nathan Sanders
+4  A: 

How often are you going to do this; if you really want an array, you should use an array. Otherwise, yes, a function that makes a new list consisting of a copy of the first N elements, the new element, and the tail will be fine. I don't know of a builtin off the top of my head, but I haven't programmed in Lisp in a while.

Here is a solution in Scheme (because I know that better than Common Lisp, and have an interpreter for checking my work):

(define (replace-nth list n elem)
  (cond
    ((null? list) ())
    ((eq? n 0) (cons elem list))
    (#t (cons (car list) (replace-nth (cdr list) (- n 1) elem)))))
hazzen
Your link on arrays is to CLtL, a Common Lisp text. But your text is Scheme code. The question is unclear on what dialect it wants (although it's tagged common-lisp). It would help if you'd note that arrays are for CL and your example code is for Scheme.
Nathan Sanders
Very true; changed to reflect that.
hazzen
+5  A: 

Does

(setf (nth N L) T)

work ?

(by the way - I'm a lisp noob and don't currently have access to a lisp vm to check this)

l0st3d
yes, except you can't use t as a variable name in common lisp since it is also the truth symbol.
inglesp
+2  A: 

hazzen's advice is good (use arrays) since you probably want to do a lot of these destructive updates and lists are very inefficient at random access. The easiest way to do this

(setq A (make-array 5) :initial-contents '(4 3 0 2 1))
(setf (elt 2 A) 'not-a-number)

where A is an array (although elt works for any sequence).

However, if you must be functional, that is

  1. You want to keep around both the old and new lists
  2. You want the old and new to share as much memory as possible.

Then you should use the Common Lisp equivalent of hazzen's code:

(defun replace1 (list n elem)
  (cond
    ((null list) ())
    ((= n 0) (cons elem list))
    (t (cons (car list) (replace1 (cdr list) (1- n) elem)))))

This looks slow because it is, and that's probably why it's not included in the standard.

hazzen's code is the Scheme version, which is useful is that's what you're using.

Nathan Sanders
A: 

The obvious solution is slow and uses memory, as noted by others. If possible, you should try to defer replacing the element(s) until you need to perform another element-wise operation on the list, e.g. (loop for x in list do ...).

That way, you'll amortize away the consing (memory) and the iteration (cpu).

Mikael Jansson
+1  A: 
(setf (nth N L) T)

is the clearest, most succinct, and fastest way, if what you want to do is a "destructive" modification, i.e. actually change the existing list. It does not allocate any new memory.

Dan Weinreb
+2  A: 

I just try to fix hazzen's code:

(define (replace-nth list n elem)
  (cond 
    ((null? list) ())
    ((eq? n 0) (cons elem list))
    (#t (cons(car list) (replace-nth (cdr list) (- n 1) elem)))))

> (replace-nth (list 3 2 9 2) 2 8)
(3 2 8 9 2)

This code inserted new element in the list. If we want to replace an element:

(define (replace-nth list n elem)
  (cond 
    ((null? list) ())
    ((eq? n 0) (cons elem (cdr list)))
    (#t (cons(car list) (replace-nth (cdr list) (- n 1) elem)))))

> (replace-nth (list 3 2 9 2) 2 8)
(3 2 8 2)

0 <= n <= length(list) - 1

A: 

Use [REPLACE][1] (I use X instead of your T as T is the true value in Lisp):

(replace L (list X) :start1 N)

[1]: http://www.lispworks.com/documentation/HyperSpec/Body/f_replac.htm REPLACE