Exercise about substitution in nested lisp [duplicate] - lisp

I’m trying to write a function with two arguments of this type:
substitutions (list_one, list_two)
list_one has always this form (letters can change according to the input):
(1 ((1 2 ((1 2 r) (3 2 t) (4 3 c))) (3 4 ((5 6 y) (5 7 i)))))
list_two has always this form (numbers can change according to the input):
(2 3 4 5 6)
I want to substitute in this way:
r-> 2
t -> 3
c -> 4
y -> 5
i -> 6
Can you help me please?

A not so efficient solution is to first find a list of all the letters in the fist tree structure (the first list) and then to LOOP over the results calling SUBST repeatedly.
To find the list of non numeric atoms in the first list (the 'letters') you need to traverse the tree structure (le first list) recurring both on the FIRST and on the REST of the list.
Hope it helps.
MA

If the lists are proper you can iterate them with the loop macro and pop off the arguments in the accessible free variable:
(defun template-replace (template replacements)
(labels ((iterate (template)
(loop :for element :in template
:collect
(cond ((consp element) (iterate element))
((symbolp element) (pop replacements))
(t element)))))
(iterate template)))
(template-replace '(1 rep (4 rep (9 rep)) rep) '(foot inch mm multiplied))
; ==> (1 foot (4 inch (9 mm)) multiplied)

Related

Lisp: How to set element in list of lists?

I am familiar with how to set elements in a 2D array, which can be done using the following statement.
(setf (aref array2D 0 0) 3)
However, I am not familiar how to set elements in a list of lists, such as the following input: '((1) (2) (2) (1)). I can't use aref, since it only works on arrays.
As mentioned, while aref works on arrays, elt works on sequences which can be:
an ordered collection of elements
a vector or a list.
* (setf test-list '((1) (2) (2) (1)))
((1) (2) (2) (1))
* (setf (elt test-list 2) 'hi)
HI
* test-list
((1) (2) HI (1))
You can indeed use variables in place of fixed offsets:
* (setf test-list '((1) (2) (2) (1)))
((1) (2) (2) (1))
* (setf offset 2)
2
* (setf (elt test-list offset) 'hi)
HI
* test-list
((1) (2) HI (1))
To access the nth element of a list, there are (at least) two functions: nth and elt. The order of the parameters is different, and nth only work on lists while elt works on any sequence (i.e. lists, vector, strings ...):
(nth 1 '(foo bar baz)) => BAR
(nth 1 #(foo bar baz)) => ERROR
(elt '(foo bar baz) 1) => BAR
(elt #(foo bar baz) 1) => BAR
Now, in general, the way to set a value (as opposed to simply access it) is very straightforward, and at least for built-in functions this is almost always the case: whenever you have some form FORM which retrieves some value from what is called a place, the form (setf FORM <value>) will set this element to the given <value>. This works for functions such as car, cdr, gethash, aref, slot-value, symbol-function and many others, and any combination of those.
In your example, you have a list of lists. So, for example, to modify the "inner integer" in say the third list:
* (setf test-list '((0) (1) (2) (3))) ; changed the values to have something clearer
((0) (1) (2) (3))
* (car (nth 2 test-list)) ; this accesses the integer in the second list
2
* (setf (car (nth 2 test-list)) 12) ; this modifies it. Notice the syntax
12
* test-list
((0) (1) (12) (3))
On a side note, you should avoid modifying literal lists (created using the quote symbol '). If you want to modify lists, create them at runtime using the list function.
EDIT:
What happens is that setf knows, by "looking" at the form you give it, how to actually find the place that you want to modify, potentially using functions in this process.
If you look at other languages, such as Python, you also have some kind of duality in the syntax used both to get and to set values. Indeed, if you have a list L or a dictionary d, then L[index] and d[thing] will get the corresponding element while L[index] = 12 and d[thing] = "hello" will modify it.
However, in Python, those accessors use a special syntax, namely, the squares brackets []. Other types of objects use another syntax, for example, the dot notation to access slots/attributes of an object as in my-object.attr. A consequence is that the following code is invalid in Python:
>>> L = [1, 2, 3, 2, 1]
>>> max(L)
3
>>> max(L) = 12
Traceback (most recent call last):
File "<string>", line 9, in __PYTHON_EL_eval
File "/usr/lib/python3.8/ast.py", line 47, in parse
return compile(source, filename, mode, flags,
File "<string>", line 1
SyntaxError: cannot assign to function call
You have to write an other function, for example, setMax(L, val), to change the maximum of a list. This means that you now have to functions, and no symmetry anymore.
In Common Lisp, everything is (at least syntactically) a function call. This means that you can define new ways to access and modify things, for any function ! As a (bad) example of what you could do:
* (defun my-max (list)
(reduce #'max list))
MY-MAX
* (my-max '(1 2 3 8 4 5))
8
* (defun (setf my-max) (val list)
(do ((cur list (cdr cur))
(cur-max list (if (< (car cur-max) (car cur))
cur
cur-max)))
((endp (cdr cur)) (setf (car cur-max) val))))
(SETF MY-MAX)
* (setf test-list (list 0 4 5 2 3 8 6 3))
(0 4 5 2 3 8 6 3)
* (setf (my-max test-list) 42)
42
* test-list
(0 4 5 2 3 42 6 3)
This way, the syntax used to both set and get the maximum of a list is identical (FORM to get, (setf FORM val) to set), and combines automatically with every other "setter". No explicit pointers/references involved, it's just functions.

push macro storing in-function but not out of function [duplicate]

This question already has answers here:
Common lisp push from function
(4 answers)
Closed 3 years ago.
I have a function:
(defun multi-push (L P)
(print (if L "T" "F"))
(print P)
(when L
(multi-push (cdr L) (push (car L) P)))
P)
which I have made in an to attempt to push a list onto another list (I am aware the input list L is reversed. This is by design). The print statements make sense, but when I look at the variable P, it is not mutated as I expect.
Sample REPL output:
CL-USER> bob
(3 3 3)
CL-USER> (multi-push (list 1 2) bob)
"T"
(3 3 3)
"T"
(1 3 3 3)
"F"
(2 1 3 3 3)
(1 3 3 3)
CL-USER> bob
(3 3 3)
What have I done wrong? I thought PUSH (according to [http://clhs.lisp.se/Body/m_push.htm]) mutates its second argument in place. I have also tried variations where I POP L and PUSH it onto P before calling multi-push on L and P again.
one thing of note is that the line (1 3 3 3) is the output of the function of multi-push. This also confuses me.
What push mutates destructively is a binding, not a list. More correctly what push modifies is a 'place' which is
a form which is suitable for use as a generalized reference
where a 'generalized reference' is
a reference to a location storing an object as if to a variable.
These two quotes are from the CLHS glossary: the section which talks about this is 5.1.
In particular:
> (let* ((l1 '(1 2 3))
(l2 l1))
(push 0 l1)
(values l1 l2))
(0 1 2 3)
(1 2 3)
And also note that this is legal CL since it doesn't destructively alter the quoted list structure. push must be a macro since a function can't do what it does: you can't write a function f such that:
(let* ((a (list 1 2 3))
(b a))
(f a b)
(not (eq a b)))
would be true.
You can think of (push x y) as expanding to something like (setf y (cons x y)), except that it will deal with multiple-evaluation properly.

List of lists in racket

I need to create this:
Define a min&max-lists function that consumes a list of lists
(where the type of the elements in the inner list may be any type).
The function returns a list of lists – such that for each inner list (in the
original list) the following is done –
If the list contains at least one number, then the list is replaced with a list of size two, containing the minimum and maximum in the list.
Otherwise, the list is replaced with a null.
For example
written in a form of a test that you can use:
(test (min&max-lists '((any "Benny" 10 OP 8) (any "Benny" OP (2 3))))
=> '((8 10) ()))
(test (min&max-lists '((2 5 1 5 L) (4 5 6 7 3 2 1) ())) >> '((1 5) (1 7) ()))
For now, I have created a function that do it for one list.
How I do it for the list of lists??
for example:
(listhelp '(2 5 1 5 L))
-> : (Listof Number)>>'(1 5)
Given that you have min&max with the strange name listhelp you can use map, use for/list, or roll your own recursion:
(define (min&max-lists lol)
(map min&max lol))
(define (min&max-lists lol)
(for/list ([e (in-list lol)])
(min&max e)))
(define (min&max-lists lol)
(if (null? lol)
'()
(cons (min&max (car lol))
(min&max-lists (cdr lol)))))

Substitutions in Common Lisp

I’m trying to write a function with two arguments of this type:
substitutions (list_one, list_two)
list_one has always this form (letters can change according to the input):
(1 ((1 2 ((1 2 r) (3 2 t) (4 3 c))) (3 4 ((5 6 y) (5 7 i)))))
list_two has always this form (numbers can change according to the input):
(2 3 4 5 6)
I want to substitute in this way:
r-> 2
t -> 3
c -> 4
y -> 5
i -> 6
Can you help me please?
A not so efficient solution is to first find a list of all the letters in the fist tree structure (the first list) and then to LOOP over the results calling SUBST repeatedly.
To find the list of non numeric atoms in the first list (the 'letters') you need to traverse the tree structure (le first list) recurring both on the FIRST and on the REST of the list.
Hope it helps.
MA
If the lists are proper you can iterate them with the loop macro and pop off the arguments in the accessible free variable:
(defun template-replace (template replacements)
(labels ((iterate (template)
(loop :for element :in template
:collect
(cond ((consp element) (iterate element))
((symbolp element) (pop replacements))
(t element)))))
(iterate template)))
(template-replace '(1 rep (4 rep (9 rep)) rep) '(foot inch mm multiplied))
; ==> (1 foot (4 inch (9 mm)) multiplied)

DELETE + SETF inside a function

I'm trying to write a function that will destructively remove N elements from a list and return them. The code I came up with (see below) looks fine, except the SETF is not working the way I intended.
(defun pick (n from)
"Deletes (destructively) n random items from FROM list and returns them"
(loop with removed = nil
for i below (min n (length from)) do
(let ((to-delete (alexandria:random-elt from)))
(setf from (delete to-delete from :count 1 :test #'equal)
removed (nconc removed (list to-delete))))
finally (return removed)))
For most cases, this works just fine:
CL-USER> (defparameter foo (loop for i below 10 collect i))
CL-USER> (pick 3 foo)
(1 3 6)
CL-USER> foo
(0 2 4 5 7 8 9)
CL-USER> (pick 3 foo)
(8 7 0)
CL-USER> foo
(0 2 4 5 9)
As you can see, PICK works just fine (on SBCL) unless the element being picked happens to be the first on the list. In that case, it doesn't get deleted. That's because the only reassignment happening is the one that goes on inside DELETE. The SETF doesn't work properly (i.e. if I use REMOVE instead, FOO does not change at all).
Is there any scoping rule going on that I'm not aware of?
A (proper) list consists of cons cells that each hold a reference to the next
cell. So, it is actually a chain of references, and your variable has a
reference to the first cell. To make this clear, I rename the binding outside
of your function to var:
var ---> [a|]--->[b|]--->[c|nil]
When you pass the value of the variable to your function, the parameter gets
bound to the same reference.
var ---> [a|]--->[b|]--->[c|nil]
/
from --'
You can update the references in the chain, for example eliminate b:
var ---> [a|]--->[c|nil]
/
from --'
This has an effect on the list that var sees outside.
If you change the first reference, for example eliminating a, this is just the
one originating from from:
var ---> [a|]--->[b|]--->[c|nil]
/
from --'
This has obviously no effect on what var sees.
You need to actually update the variable binding in question. You can do that
by setting it to a value returned by function. Since you already return a
different value, this would then be an additional return value.
(defun pick (n list)
(;; … separate picked and rest, then
(values picked rest)))
Which you then use like this, for example:
(let ((var (list 1 2 3)))
(multiple-value-bind (picked rest) (pick 2 var)
(setf var rest)
(do-something-with picked var)))
Now to the separation: unless the list is prohibitively long, I'd stick to
non-destructive operations. I also would not use random-elt, because it needs
to traverse O(m) elements each time (m being the size of the list),
resulting in a runtime of O(n·m).
You can get O(m) overall runtime by determining the current chance of picking
the current item while linearly running over the list. You then collect the
item into either the picked or rest list.
(defun pick (n list)
(loop :for e :in list
:and l :downfrom (length list)
:when (or (zerop n)
(>= (random 1.0) (/ n l)))
:collect e :into rest
:else
:collect e :into picked
:and :do (decf n)
:finally (return (values picked rest))))
Delete isn't required to modify any structure, it's just permitted to. In fact, you can't always do a destructive delete. If you wanted to delete 42 from (42), you'd need to return the empty list (), which is the symbol NIL, but there's no way that you can turn the list (42), which is a cons cell (42 . NIL) into a different type of object (the symbol NIL). As such, you'll probably need to return both the updated list, as well as the elements that were removed. You can do that with something like this, which returns multiple values:
(defun pick (n from)
(do ((elements '()))
((or (endp from) (zerop n))
(values elements from))
(let ((element (alexandria:random-elt from)))
(setf from (delete element from)
elements (list* element elements))
(decf n))))
CL-USER> (pick 3 (list 1 2 3 2 3 4 4 5 6))
(2 6 4)
(1 3 3 5)
CL-USER> (pick 3 (list 1 2 3 4 5 6 7))
(2 7 5)
(1 3 4 6)
CL-USER> (pick 2 (list 1 2 3))
(2 3)
(1)
CL-USER> (pick 2 (list 1))
(1)
NIL
On the receiving end, you'll want to use something like multiple-value-bind or multiple-value-setq:
(let ((from (list 1 2 3 4 5 6 7)))
(multiple-value-bind (removed from)
(pick 2 from)
(format t "removed: ~a, from: ~a" removed from)))
; removed: (7 4), from: (1 2 3 5 6)
(let ((from (list 1 2 3 4 5 6 7))
(removed '()))
(multiple-value-setq (removed from) (pick 2 from))
(format t "removed: ~a, from: ~a" removed from))
; removed: (3 5), from: (1 2 4 6 7)
delete does not necessarily modify its sequence argument. As the hyperspec says:
delete, delete-if, and delete-if-not return a sequence of the same type as sequence that has the same elements except that those in the subsequence bounded by start and end and satisfying the test have been deleted. Sequence may be destroyed and used to construct the result; however, the result might or might not be identical to sequence.
For instance, in SBCL:
* (defvar f (loop for i below 10 collect i))
F
* (defvar g (delete 0 f :count 1 :test #'equal))
G
* g
(1 2 3 4 5 6 7 8 9)
* f
(0 1 2 3 4 5 6 7 8 9)
*
Note that in your function setf modifies the local variable from, and since delete in the case of first element does not modify the original list, at the end of the function the variable foo maintains the old values.