Association list in another association list - lisp

Is there any way to make something in lisp that can do like an association list in another association list, I tried :
(setq alist '((A . B) (B . C) (C . (D . E))))
but it gives :
((A . B) (B . C) (C D . E))
and then do a something like:
(assoc 'd (assoc 'c alist))
and i get this error:
Maximum error depth exceeded (22 nested errors) with
'The value C is not of type LIST.'.

((A . B) (B . C) (C . (D . E))) is not a nested assoc list.
((A . B)
(B . C)
(C . (D . E)) ; <- (d . e) is not an assoc list. Just one association.
)
You want to have a list of associations: ((d . e)).
Which makes it this solution:
CL-USER 5 > (assoc 'C '((A . B) (B . C) (C . ((D . E)))))
(C (D . E))
CL-USER 6 > (assoc 'd (cdr (assoc 'C '((A . B) (B . C) (C . ((D . E)))))))
(D . E)
Note that '(C . (D . E)) and (C D . E) are both lists of the same structure, just differently written:
CL-USER 8 > (equal '(C . (D . E)) '(C D . E))
T

I think i found it,
(setq alist '((A . B) (B . C) (C . ((D . E)))))
(assoc 'd ( cdr ( assoc 'c alist))) => (D . E)

Related

how to map an element in a list to a value from other list in LISP

I am new to lisp programming and i am trying to think about the below operation.
(extract '(0 1 0) '(a b c)) give us '(a b a)
(extract '(1 1 1 ) '(a b c)) gives us '(b b b)
how can i think about this and how to solve it.
As Chris Jester-Young described, it just returns elements from second list at indexes in first list. Writing such a function is very easy:
(defun extract (list-1 list-2)
(mapcar (lambda (n) (nth n list-2)) list-1))
CL-USER>(extract '(0 1 0) '(a b c))
(A B A)
CL-USER>(extract '(1 1 1 ) '(a b c))
(B B B)
If there no such index, it'll give you NIL in that place.
CL-USER> (extract '(1 100 1 ) '(a b c))
(B NIL B)
But this won't work on nested structures (trees). If you want it to return elements of list-2 shaped in the structure of list-1, you can use a simple maptree helper function, then do the same thing:
(defun maptree (fn tree)
(cond
((null tree) tree)
((atom tree) (funcall fn tree))
(t (cons
(maptree fn (first tree))
(maptree fn (rest tree))))))
(defun extract* (list-1 list-2)
(maptree (lambda (n)
(nth n list-2)) list-1))
CL-USER> (extract* '(3 (2 1 (0))) '(a b c d))
(D (C B (A)))
(extract a b) returns a copy of a where each element is replaced by the element of b in that position.

Adding two alists by values in elisp

I have two lists of dotted-pairs of the form:
(((key1 . value1) . 5)
((key2 . value2) . 7))
(((key2 . value2) . 3)
((key3 . value3) . 9))
i want the result to be an association list:
(((key1 . value1) . 5)
((key2 . value2) . 10)
((key3 . value3) . 9))
How do i add two association lists by values in emacs lisp? In other words, if two alists have the same key, then values for that key should be added together in resulting alist.
The most probable answer for this is some elisp snippet, but i would also prefer a nifty emacs macro.
A solution using the CL module (written with readability rather than efficiency in mind):
(require 'cl)
(defun merge-alists (function default alist1 alist2)
(flet ((keys (alist) (mapcar #'car alist))
(lookup (key alist) (or (cdr (assoc key alist)) default)))
(loop with keys = (union (keys alist1) (keys alist2) :test 'equal)
for k in keys collect
(cons k (funcall function (lookup k alist1) (lookup k alist2))))))
You can use it like this:
elisp> (merge-alists '+ 0 '((foo . 1) (bar . 2)) '((foo . 11) (baz . 22)))
((baz . 22)
(foo . 12)
(bar . 2))
elisp> (merge-alists '* 1 '((foo . 1) (bar . 2)) '((foo . 11) (baz . 22)))
((baz . 22)
(foo . 11)
(bar . 2))
elisp> (merge-alists 'append '() '((foo a b) (bar c)) '((foo d e) (baz f g)))
((baz f g)
(foo a b d e)
(bar c))
elisp> (setq my-alist1 '(((key1 . value1) . 5) ((key2 . value2) . 7)))
(((key1 . value1) . 5)
((key2 . value2) . 7))
elisp> (setq my-alist2 '(((key2 . value2) . 3) ((key3 . value3) . 9)))
(((key2 . value2) . 3)
((key3 . value3) . 9))
elisp> (merge-alists '+ 0 my-alist1 my-alist2)
(((key3 . value3) . 9)
((key1 . value1) . 5)
((key2 . value2) . 10))
It could be something like:
(defun merge-alists (a1 a2)
(let ((ac (copy-alist a1)))
(dolist (x a2)
(when (null (assoc (car x) ac))
(add-to-list 'ac x)))
ac))
it will return copy of first list with data added from second list...
But if you'll use simple append function, then duplicates won't be found anyway, as functions usually return first found value, not all that exist.
Update: sorry, incorrectly read question, another answers do it correctly, and in more general way... Although, here is my variant of this function, without using CL package:
(defun merge-alists (a1 a2)
(let ((ac (copy-alist a1)))
(dolist (x a2)
(let ((r (assoc (car x) ac)))
(if (null r)
(add-to-list 'ac x)
(setf (cdr r) (+ (cdr x) (cdr r))))))
ac))
P.S. in your example, parentheses are unbalanced :-)

Mapcar and assoc

I would like to do:
(mapcar #'assoc '(a s) '((a . b) (c . d) (s . f)))
and have it return
((A . B) (S . F))
Which seems pretty reasonable, considering (assoc 'a '((a . b) (c . d) (s . f))) returns (A . B) and (assoc 's '((a . b) (c . d) (s . f))) returns (S . F). But alas it does not work:
*** - ASSOC: A is not a list
The following restarts are available:
ABORT :R1 Abort main loop
Any thoughts?
When used with two lists, mapcar applies the function pair-wise to the lists (and with three lists it applies them triple-wise etc.). So
(mapcar #'assoc '(a s) '((a . b) (c . d) (s . f)))
is the same as
( (assoc 'a (a . b)) (assoc 's (c . d)) )
(when used with lists of different length, mapcar uses the size of the smallest list). To get what you want, you should do:
(mapcar (lambda (x) (assoc x '((a . b) (c . d) (s . f)))) '(a s))
We need another list level. The second argument should be a list of assoc lists.
CL-USER > (mapcar #'assoc '(a s) '(((a . b) (c . d) (s . f))))
((A . B))
But the second argument is only one element long. Now we can use a trick and make it a circular list:
CL-USER > (mapcar #'assoc '(a s) '#1=(((A . B) (C . D) (S . F)) . #1#))
((A . B) (S . F))
If we construct a circular list for the second argument, then it works.
As a function:
(defun circular (list)
(if (null list)
list
(setf (cdr (last list)) list)))
CL-USER > (mapcar #'assoc '(a s) (circular '(((a . b) (c . d) (s . f)))))
((A . B) (S . F))

How to do ((A.B).(C.D)) in lisp

I'm trying to figure out how to do this using cons:
((A . B) . (C . D))
where (A . B) and (C . D) are in each cons cell
I've tried doing this (cons (cons 'a 'b) (cons 'c 'd)) but it gives me this:
((A.B) C . D)
I also tried this: (cons (cons 'a 'b) (cons (cons 'c 'd) ())) but it gives me this:
((A . B) (C . D))
Any idea how to achieve this?
The first one is what you want. They're equivalent. You can verify like this:
1 ]=> (cons (cons 'a 'b) (cons 'c 'd))
;Value 11: ((a . b) c . d)
1 ]=> (car (cons (cons 'a 'b) (cons 'c 'd)))
;Value 12: (a . b)
1 ]=> (cdr (cons (cons 'a 'b) (cons 'c 'd)))
;Value 13: (c . d)
Remember a list is a cons cell. The "car" is the head element of the list or the first half of the cons cell, and the cdr is the rest of the list, or the second element of the cons cell.
Another way to verify that they're equivalent:
1 ]=> '((a . b) . (c . d))
;Value 14: ((a . b) c . d)
Just look at what you get back when you enter in a literal ((A . B) . (C . D)):
* '((a . b) . (c . d))
((A . B) C . D)
There is a defined algorithm the Lisp printer uses to print out data structures built from pairs. Basically, you can't ever get a cons to be printed as a dotted pair inside parentheses when it is the CDR of another cons.
However, it is possible to re-configure the printer so that you get the behavior you are seeking, via SET-PPRINT-DISPATCH:
(set-pprint-dispatch 'cons
(lambda (stream object)
(format stream "(~W . ~W)" (car object) (cdr object))))
* '((a . b) . (c . d))
((A . B) . (C . D))
* (cons (cons 'a 'b) (cons 'c 'd)) ;The same object
((A . B) . (C . D))
Although in spite of that it would frankly be better in the long run if you got comfortable with reading the default behavior.
I'm not quite sure what you mean... I agree with the above comment that the last line of your code resembles the first, which you are matching against.
Here's a decent general resource for you anyhow: http://www-2.cs.cmu.edu/~dst/LispBook/
What you're looking for isn't possible because of how lists are represented in Lisp. When you create a list, you are creating a series of cons cells, where the car of the cell is the value of that element in the list, and the cdr is a reference to the next cons cell. Your desired cell, ((A . B) . (C . D)) means "create a cons cell where the car is (A . B) and the cdr is (C . D)". That is equivalent to a list where the first element is (A . B), second element is C and the tail of the list is D, or ((A . B) C . D).

Implementing Micro Manual LISP

I am implementing an interpreter for the LISP defined in,
http://www.scribd.com/vacuum?url=http://www.ee.ryerson.ca/~elf/pub/misc/micromanualLISP.pdf
My problem is the paper states that a LIST is,
4. (LIST e1 ... en) is defined for each n to be
(CONS e1 (CONS ... (CONS en NIL))).
So when a read in a list from the user such as,
(QUOTE (B C D (E F)))
using the above structure it becomes,
(QUOTE B C D E F)
There is no way to differentiate nested lists it all becomes a giant chain of cons.
Am I missing something here?
(QUOTE (B C D (E F))) is
(CONS B (CONS C (CONS D (CONS (CONS E (CONS F NIL)) NIL))))
(QUOTE (B C D E F)) is
(CONS B (CONS C (CONS D (CONS E (CONS F NIL)) NIL)))
Or to put it another way:
(LIST D (LIST E F)) = (CONS D (CONS (LIST E F) NIL))
(LIST D E F) = (CONS D (LIST E F))
(QUOTE (B C D (E F)))
= (... (CONS (E F) NIL))).
= (... (CONS (CONS E (CONS F NIL)) NIL))).
which is different from
( ...(CONS D (CONS E (CONS F NIL))).
(QUOTE (B C D (E F))) = (LIST B C D (LIST E F))