Strange behavior invoking destructive Common LISP function receiving as argument a list created with quote - list

I've been getting a strange behavior when invoking a destructive definition receiving as argument a local variable whose type is a list created with a quote.
Destructive function:
(defun insert-at-pos (pos list elem)
(if (= pos 0)
(cons elem list)
(let ((aux-list (nthcdr (1- pos) list)))
(setf (rest aux-list) (cons elem (rest aux-list)))
list)))
WRONG: Local variable is a list created with the special operator quote.
(defun test ()
(let ((l '(1 2 3)))
(print l)
(insert-at-pos 2 l 4)
(print l)))
> (test)
(1 2 3)
(1 2 4 3)
(1 2 4 3)
> (test)
(1 2 4 3)
(1 2 4 4 3)
(1 2 4 4 3)
> (test)
(1 2 4 4 3)
(1 2 4 4 4 3)
(1 2 4 4 4 3)
CORRECT: Local variable is a list created with function list.
(defun test2 ()
(let ((l (list 1 2 3)))
(print l)
(insert-at-pos 2 l 4)
(print l)))
or
(defun test2 ()
(let ((l '(1 2 3)))
(print l)
(setf l (cons (first l) (cons (second l) (cons 4 (nthcdr 2 l)))))
(print l)))
> (test2)
(1 2 3)
(1 2 4 3)
(1 2 4 3)
> (test2)
(1 2 3)
(1 2 4 3)
(1 2 4 3)
> (test2)
(1 2 3)
(1 2 4 3)
(1 2 4 3)
Does someone know the reason of this strange behaviour?

If you quote data in a function, then it is literal data. The effects of destructively modifying such literal data are undefined in the Common Lisp standard. In your example all function invocations share the same literal data and the implementation does not warn you that you are changing it. That's what most implementations do. But it would also possible to imagine an implementation which puts all code (and its literal data) into a read-only part of the memory.
You can get funky effects with this.
If you want to destructively modify a list without running into potential problems, then you need to create a fresh copy at runtime. For example by calling LIST or COPY-LIST. LIST will return a fresh consed list.
There are similar pitfalls. For example imagine a file with these definitions:
(defvar *foo* '(1 2 3 4 5 6 ... 10000))
(defvar *foo* '(0 1 2 3 4 5 6 ... 10000))
If you compile such a file with the file compiler, the compiler is allowed to create a compiled file, where the two variables share literal data - saving space. If you would change an element in either list, both might be changed.

Related

Advice on how to tackle this lisp function.

I have written a function called my_rotate that takes a number from a user and creates a list up to five numbers. my_rotate then, pops off the first element of the list and adds it to the end of the list. Any advice on how I could write my_rotate to take in another number n and rotate the list based on the number n, in which the user entered.
Example:
> (my_rotate 1 2)
Outputs:
(3 4 5 1 2)
This is what I have so far:
(defun my_rotate (y)
(append (loop for i from (+ 1 y) to (+ 4 y) collect i)
(list y)))
Here the function.
I create two lists and then concatenate them.
(defun my-rotate (length shift)
"Return a list of given LENGTH, rotated by SHIFT."
(nconc
(loop for i from (1+ shift) to (- length shift -2) collect i)
(loop for i from 1 to shift collect i)))
(my-rotate 7 2)
==> (3 4 5 6 7 1 2)
Note that since both loops produce fresh lists, I use nconc instead of append.
If, however, you want to rotate an existing list, you will need to do something else:
(defun rotate-list (list shift)
"Rotate the given LIST by the specified SHIFT."
(let ((len (length list)))
(setq shift (mod shift len)) ; handle circular shifts
(append (nthcdr (- len shift) list)
(butlast list shift))))
(rotate-list '(1 2 3 4 5 6) 2)
==> (5 6 1 2 3 4)
(rotate-list '(1 2 3 4 5 6) 20)
==> (5 6 1 2 3 4) ; same because 20 = 2 mod 6
(rotate-list '(1 2 3 4 5 6) 0)
==> (1 2 3 4 5 6) ; unchanged
Note that nthcdr points inside the original list, so we have to use append to avoid modifying the argument.
Note also that we scan the list argument twice (once in nthcdr and once in butlast).
If your lists are huge, and profiling shows that this function is the bottleneck, you might want to re-write this using a loop (this is scenario is so unlikely, that I already regret having wasted my time writing this note).

Recursive functions that rotate n elements of a list to left and right in lisp

The function has 1 parameter, an integer.
For example rot-left(2 '(1 2 3 4 5)) should return (3 4 5 1 2 ) and rot-right(2 '(1 2 3 4 5)) should return (5 4 1 2 3).
I've tried this... it doesn't work but what it's supposed to do is add the last n elements of a list to an empty list.
(defun rot_left (n l)
(if (zerop n)
'()
(append (last l)
rot-left ((- n 1) (cdr l)))))
I will give a solution assuming that, if the function rot-right should rotate the elements of the list from right to left, (rot-right 2 '(1 2 3 4 5)) should produce (4 5 1 2 3) and not (5 4 1 2 3).
Then, assuming that this interpretation is correct, the functions can be written only by means of primitive operators in Common Lisp, without the use of iteration or recursion:
(defun rot-left(n l)
(append (nthcdr n l) (butlast l (- (length l) n))))
(defun rot-right(n l)
(rot-left (- (length l) n) l))
(defvar a '(1 2 3 4 5))
(rot-left 2 a) ; produces (3 4 5 1 2)
(rot-right 2 a) ; produces (4 5 1 2 3)

opposite of list-ref? (Racket)

Is there anything which acts as the opposite of list-ref, where instead of selecting certain values to add to a list, it'll take values away from a list?
I basically want to do the following
(list 1 2 3 4 5 6 7) (list 3 6 7) -> (list 1 2 4 5)
Where the values in list two get deleted from list one. (preferred)
Since I will always start with a list that goes from 1 to n,
the second list could also represent the location/position where a number on list 1 should be deleted. (less preferred)
I'm trying to create a code which will manipulate other functions to come up with these lists, so please be clear where each list is 'mentioned' in the code, as I sometimes get confused if people use x y and z and so forth with multiple lambda, local definitions, etc.
I have something here which does the opposite of what I want and I've been trying to alter it so instead of outputting the elements of x that are on y, it gives the elements of x which are NOT on y.
(define (selection x y)
(filter (lambda (e2)
(ormap (lambda (e1) (equal? e1 e2))
y))
x))
example:
(list 1 2 3 4 5 6 7 8 9 10)
(list 2 4 6 8 10))
-> (list 2 4 6 8 10))
Anybody have any ideas on how to change the output to what I need?
It sounds like you're using lists as sets. You could instead use Racket sets, and use the set-subtract function:
#lang racket
(set-subtract (set 1 2 3 4 5 6 7)
(set 3 6 7))
;; => (set 1 2 4 5)
remove will do the trick I guess.
> (remove* (list 1 2) (list 1 2 3 2 4 5 2))
'(3 4 5)
You can read the doc here.
Here's a simple recursive function that achieves what you want:
(define remove-list-from-list (lambda (list remlist)
(cond
[(null? list) '()]
[(member (car list) remlist) (remove-list-from-list (cdr list) remlist)]
[else (cons (car list) (remove-list-from-list (cdr list) remlist))])))
Now you can use it like so:
> (remove-list-from-list (list 1 2 3 4 5 6 7) (list 3 6 7))
'(1 2 4 5)

Scheme: change value of an element in a list

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!
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.)
Using standard functions without any SRFI:
(set-car! (list-tail lst k) val)
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.)
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.

Scheme How to Take the first item in the List?

Say I want to take the first item of the lists '(4 3 1) '(5 6 8)
I want something like this
(first '(4 3 1) '(5 6 8))
should return me the first item
(4 3 1)
as result. Is there something like this in scheme build-in function I can call ?
car doesn't work, as it only returns me the first item inside 1 list
list-ref doesn't work, same reason above, returns me 1 item inside the list
How can I do that? if I need to write it myself this first function ?
You can use the list-ref procedure to get an element from a list, using its index, for example:
(let ((l '((4 3 1) (5 6 8))))
(list-ref l 0)) ; get the element at index 0
However if you only want the first element, you can use car:
(let ((l '((4 3 1) (5 6 8))))
(car l))
Check the snippet running here.
You can use "." in function definition to take arbitrary number of arguments.
(define (first . args) (car args))
(first '(4 3 1) '(5 6 8)) ;; => (4 3 1)
You're trying too hard:
> (define (first a b) a)
> (first '(1 2 3) '(4 5 6))
(1 2 3)
It's been a while since I used scheme, but wouldn't you need to have the lists in their own list first
(first (list '(4 3 1) '(5 6 8)))