Common Lisp: Why not the array literal evaluate arguments? - lisp

Why is it that the Common Lisp array syntax is not evaluating its arguments:
(let ((a 1)) #2A((a 2) (3 4)))
=> #2A((A 2) (3 4))
I would have guessed it was #2A((1 2) (3 4)). Is this because A is not available at reader time?

In short, yes.
#2A((A 2) (3 4)) is not an abbreviation ("syntactic sugar") for (make-array '(2 2) :initial-contents (list (list a 2) (list 3 4))). If anything, it could be rationalized as (make-array '(2 2) :initial-contents (quote ((A 2) (3 4)))), but this would be a bit misleading as the array construction already happens at read-time.

Related

Mapping to a nested list, function applies to quote

I have something like this
(map (lambda (l) (apply + l)) '('(1 2) '(3 4)))
I expect '(3 7), however I get an error saying that the applied function gets applied to a quote.
Why does this happen?
The list I had was equivalent to
(list (quote (list 1 2)) (quote (list 3 4)))
and not
(list (list 1 2) (list 3 4))
The list was malformed. For the difference between list and quote see this post: What is the difference between quote and list?.
A proper way to do nested loop is like '((1 2) (3 4)).

Find and remove a list item based on the item's third element

I need to find and return the first item in a list when that item's third element matches a value passed into the function. I then need that item to be permanently removed from the list.
I have written this function to do it and am wondering if there are any built-in functions that might accomplish the same without this fairly messy implementation:
(defun find-remove-third (x)
(let ((item (first (member x *test-list* :key #'third))))
(setf *test-list* (remove item *test-list* :test #'equal))
item))
Operation:
CL-USER> *test-list*
((1 2 3) (2 3 4) (3 4 5) (4 4 4) (5 4 3) (6 5 4) (2 2 2))
CL-USER> (find-remove-third 4)
(2 3 4)
CL-USER> *test-list*
((1 2 3) (3 4 5) (4 4 4) (5 4 3) (6 5 4) (2 2 2))
CL-USER> (find-remove-third 4)
(4 4 4)
CL-USER> *test-list*
((1 2 3) (3 4 5) (5 4 3) (6 5 4) (2 2 2))
For example pop mutates and returns from a list, though is more limited, but I'm wondering if anything more elegant than my function above might be possible, or if this implementation is normal and idiomatic?
Your implementation scans the list twice, so it is suboptimal.
I don't think you can write what you need without an explicit loop (or, equivalently, recursion):
(defun pop-from-list (object list &key (key #'identity) (test #'eql) kept)
"Like `remove', but return the object removed as the second value."
(let ((1st (car list)))
(if (funcall test object 1st)
(values (revappend kept (rest list))
1st)
(pop-from-list object (rest list) :key key :test test
:kept (cons 1st kept)))))
Now you can define your function like this:
(defun find-remove-third (x)
(multiple-value-bind (list object)
(pop-from-list x *test-list* :key #'third)
(setq *test-list* list)
object))
Edit - Deleting this doesn't seem right so I'll leave it up, but as noted by #sds and #WillNess in the comments, this has serious issues.
Here's a destructive version which only scans the list once. It has the potential benefit that you don't have to hardcode the name of the list you're operating on.
CL-USER> (defun find&remove (list obj &key (key #'identity) (test #'eql))
(loop with last = nil
for cons on list
when (funcall test obj (funcall key (first cons))) do
(progn (setf (rest last) (rest cons))
(return (first cons)))
do (setf last cons)))
CL-USER> (defvar test-list (list (list 1 2 3)
(list 3 4 5)
(list 5 6 7)
(list 8 9 10)))
CL-USER> (find&remove test-list 5 :key #'third)
(3 4 5)
CL-USER> test-list
((1 2 3) (5 6 7) (8 9 10))
CL-USER> (find&remove test-list 7 :key #'third)
(5 6 7)
CL-USER> test-list
((1 2 3) (8 9 10))
The key is to traverse the list by cons cell rather than by item (loop for ... on rather than loop for ... in) and keep a pointer to the parts of the list we've already looked at (last). Then, when we find what we're looking for, we connect the cdr of what we'd already seen to the next cons (so now the list omits the "hit") and finally return the result.

Why does this expression not work as expected in Common Lisp?

A newcomer to Lisp. I know that
(mapcar #'list '(1 2) '(3 4))
will give
'((1 3) (2 4))
and based on my understanding of how apply works, I expect
(apply #'(lambda (&rest x) (mapcar #'list x)) '((1 2) (3 4)))
to return the same result. Instead, I am getting
'(((1 2)) ((3 4)))
I am confused because
(apply #'append '((1 2) (3 4)))
gives me
'(1 2 3 4)
as expected. What is going on?
Simplify it. Suppose you used A instead of (1 2), and B instead of (3 4):
(apply #'(lambda (&rest x) (mapcar #'list x)) '(A B))
Because &rest x takes all the arguments and packs them up as a list, so x has the value
(A B).
Then mapcar iterates twice, passing A to list, producing (A), then it does the same with B. Then mapcar makes a list of those, producing ( (A) (B) )
It's useful to put print statements in there to see what's going on.

Adding two or more list based on it's pair in a list

I am not too proficient with functional style and I don't want to use any set functions, so I have a problem. I am really struggling whether I should do recursively or in a different manner.
I have a collection of pairs in a list, like so:
((4 2) (3 1) (3 2) (2 4) etc...)
In this pair '(4 2), the second element '2' tells me which other pairs it matches to, in this case '(3 2).
So, I add these two pairs together using their first element, in this case, it's '4' and '3'.
The new pair is now '(7 2). And so on for other pairs in the list too.
Finally, it should return:
((7 2) (3 1) (2 4))
I would care less about the order.
.
I already have a working function that add two different pairs. The only assumption with this function is that the pairs are matching.
Consequently, what I want to do is manipulated this list of pairs to return a list in these manners.
Examples:
take the list ((4 2) (3 1) (3 2) (2 4))
matching-pairs: '(4 2) and '(3 2)
and then return --> ((7 2) (3 1) (2 4))
take the list ((2 1) (3 2) (1 2) (5 1) (6 3))
matching-pairs: '(2 1) and '(5 1)
'(3 2) and '(1 2)
and then return --> ((7 1) (4 2) (6 3))
Thank you for your time and efforts.
Iterate over your list and store each pair's car into a list in an assoc that looks like this:
original: ((2 . 1) (3 . 2) (1 . 2) (5 . 1) (6 . 3))
new: ((1 . (2 5))
(2 . (3 1))
(3 . (6))
Then sum together all the cdrs and flip each pair to get this:
((7 . 1) (4 . 2) (6 . 3))
(defun get-pairs (alist index)
(cond
(alist
(if (= (nth 1 (car alist)) index)
(append (list (caar alist)) (get-pairs (cdr alist) index))
(get-pairs (cdr alist) index)))
((not alist)
'nil)))
(defun get-iterator (alist)
(labels
((f (alist res)
(cond
(alist
(if (member (nth 1 (car alist)) res)
(f (cdr alist) res)
(f (cdr alist) (append (cdar alist) res))))
((not alist)
res))))
(f alist 'nil)))
(defun get-value (alist)
(loop for i in (get-iterator alist)
collect (get-pairs alist i)))
(defun list-sum (alist)
(loop for i in (get-value alist)
collect (apply #'+ i)))
(defun match-value (alist)
(loop for i in (get-iterator alist)
for j in (list-sum alist)
collect (append (list j) (list i))))
(defparameter *my-list* '((2 1) (3 1) (4 2) (5 2) (8 1) (9 2) (1 3) (0 3)))
(print (match-value *my-list*))
Sorry for the messy code but that should do the trick if I understood the problem right.

Mapping a function over two lists in elisp

In common lisp I can do this:
(mapcar #'cons '(1 2 3) '(a b c))
=> ((1 . A) (2 . B) (3 . C))
How do I do the same thing in elisp? When I try, I get an error:
(wrong-number-of-arguments mapcar 3)
If elisp's mapcar can only work on one list at a time, what is the idomatic way to combine two lists into an alist?
You want mapcar*, which accepts one or more sequences (not just lists as in Common Lisp), and for one sequence argument works just like the regular mapcar.
(mapcar* #'cons '(1 2 3) '(a b c))
((1 . A) (2 . B) (3 . C))
And even if it weren’t defined, you could easily roll your own:
(defun mapcar* (f &rest xs)
"MAPCAR for multiple sequences"
(if (not (memq nil xs))
(cons (apply f (mapcar 'car xs))
(apply 'mapcar* f (mapcar 'cdr xs)))))
Emacs has built-in Common Lisp library, which introduces plenty of Common Lisp functions and macros, but with the cl- prefix. There is no reason to avoid this library. cl-mapcar is what you want:
(cl-mapcar '+ '(1 2 3) '(10 20 30)) ; (11 22 33)
With dash list manipulation library (see the installation instructions), you can use -zip-with (remember: -zip-with is the same as cl-mapcar applied to 2 lists):
(-zip-with '+ '(1 2 3) '(10 20 30)) ; (11 22 33)
I don't know an elegant way to implement a -zip-with equivalent for 3 arguments. But you may use -partial from dash-functional package, which comes with dash (functions from dash-functional require Emacs 24). -partial partially applies the function, so these 2 function invocations below are equivalent:
(-zip-with '+ '(1 2) '(10 20)) ; (11 22)
(funcall (-partial '-zip-with '+) '(1 2) '(10 20)) ; (11 22)
Then, you can use it with a -reduce function:
(-reduce (-partial '-zip-with '+) '((1 2 3) (10 20 30) (100 200 300)))
; (111 222 333)
You can wrap it into a function with &rest keyword, so this function would accept varying amount of arguments instead of a list:
(defun -map* (&rest lists)
(-reduce (-partial 'zip-with '+) lists))