How to retrieve value of cons cell by key name? - emacs

Say I have a list of cons cells like so:
(setq foo '(("a" . 1) ("b" . 2) ("c" . 3)))
And I'd like to retrieve the value of a particular cons cell by "key name". Is there a function that will let me do this?
E.g.
(get-by-key "a" foo) ;; => 1
Or something similar. Thanks in advance!

Such list is called an association list, or alist for short. Formally, an association list is a list of conses of a key and its associated value.
The assoc function is what you are looking for. It takes a key and an alist as its arguments and returns the first association for the key in the alist in terms of equal:
ELISP> (setq foo '(("a" . 1) ("b" . 2) ("c" . 3)))
(("a" . 1)
("b" . 2)
("c" . 3))
ELISP> (assoc "a" foo)
("a" . 1)
ELISP> (cdr (assoc "a" foo))
1
The assoc-string function is similar to the assoc function but specific to association lists whose keys are strings. In addition to a key and an alist, it can take another optional argument that makes the key comparison case-insensitive:
ELISP> (assoc-string "a" foo)
("a" . 1)
ELISP> (assoc-string "A" foo)
nil
ELISP> (assoc-string "A" foo t)
("a" . 1)
For the full list of association list-related functions, refer to GNU Emacs Lisp Reference Manual.

assoc-default lets you retrieve the value of a particular cons cell by "key name".
ELISP> (setq foo '(("a" . 1) ("b" . 2) ("c" . 3)))
(("a" . 1)
("b" . 2)
("c" . 3))
ELISP> (assoc-default "a" foo)
1

alist-get with the KEY and the ALIST as arguments gives you VALUE associated to the KEY.
E.g.,
(alist-get 'a '((a . 1) (b . 2) (c . 3)))
evaluates to 1.
Comparison is done with eq by default. But its full argument list is:
(alist-get KEY ALIST &optional DEFAULT REMOVE TESTFN)
So one can give:
A DEFAULT value that is returned if there is no match for KEY,
A flag REMOVE that removes the KEY VALUE pair if the new value is DEFAULT in
(setf (alist-get KEY ALIST DEFAULT t) DEFAULT)
A test function TESTFN for comparing KEY with the cars of ALIST

Related

Non destructive modify hash table

Is it possible to non-destructively add new key-value pairs to a Common Lisp (SBCL) hash table? The standard way to add new elements to a hash table is to call:
(setf (gethash key *hash-table*) value)
but the call to setf modifies *hash-table* corrupting the original. I have an application where I'd like to take advantage of the efficiency of hash table lookups, but I also would like to non destructively modify them. The work-around I see is to copy the original hash table before operating on it, but that is not practical in my case since the hash tables I'm dealing with contain many thousands of elements and copying large hash tables, say, in a loop would negate the computational efficiency advantage of using them in the first place.
Depending on your needs you may be able to just use an association list, using assoc and other functions to establish new bindings on top of existing ones. The fact that assoc returns the first matching element means you can shadow bindings:
(let ((list '((:a . 1) (:b . 2))))
(acons :b 3 list))
=> ((:b . 3) (:a . 1) (:b . 2))
If you call (assoc :b list) in the resulting list, the entry will be (:b . 3), but the original list is unmodified.
FSet
If association lists are not enough, the FSet library provides purely functional data-structures for Common Lisp, like maps, which are immutable hash-tables. They are implemented as balanced trees, which is better than a naive approach. There are also other data-structures that are more efficient, but you probably need to implement them yourselves (Hash array mapped trie (edit: see https://github.com/danshapero/cl-hamt, thanks #Flux)). That being said, FSet is good enough in general.
FSet is available through Quicklisp
USER> (ql:quickload :fset)
Create a map; notice the printed representation is made to be read again, if you install the appropriate reader macros. But you can perfectly use the library without the modified syntax table.
USER> (fset:map (:a 0) (:b 1))
#{| (:A 0) (:B 1) |}
Update the previous map with a new binding for :c:
USER> (fset:with * :c 3)
#{| (:A 0) (:B 1) (:C 3) |}
Update the previous map with a new binding for :b, which shadows the previous one:
USER> (fset:with * :b 4)
#{| (:A 0) (:B 4) (:C 3) |}
All the intermediate maps are unmodified:
USER> (list * ** *** )
(#{| (:A 0) (:B 4) (:C 3) |}
#{| (:A 0) (:B 1) (:C 3) |}
#{| (:A 0) (:B 1) |})
I don't think that you can pass-by-reference a hash-table to another hash-table in common lisp.
But what I had an idea was how to avoid to copy the entire hashtable,
yet get to a result by one call is to use the default value argument position
of gethash.
(gethash key ht default-value) returns what is given for default-value, when key is not present in ht.
;; prepare three example hash-tables, where *h3* and *h2* gets the additional keys
;; and if a key is not present in *h3*, one should look up in *h2*, and if not there too, in *h1*.
(defparameter *h1* (make-hash-table))
(setf (gethash 'a *h1*) 1)
(setf (gethash 'b *h1*) 2)
(setf (gethash 'c *h1*) 3)
(defparameter *h2* (make-hash-table))
(setf (gethash 'd *h2*) 4)
(setf (gethash 'e *h2*) 5)
(defparameter *h3* (make-hash-table))
(setf (gethash 'f *h3*) 6)
;; the call
(gethash 'a *h3* (gethash 'a *h2* (gethash 'a *h1*)))
;; would give the desired result `1`.
;; let us assume, there is a chain of hash-tables *hk* *h(k-1)* ... *h2* *h1*
;; in which one should look up into that order.
;; Then it is to us to build the code
;; (gethash 'a *hk* (gethash 'a *h(k-1)* ...(gethash 'a *h2* (gethash 'a *h1*))...))
;; automatically for every lookup.
;; this macro does it:
(defmacro mget (key hash-tables-list)
(flet ((inject-last (e1 e2) `(,#e1 ,e2)))
(reduce #'inject-last
(mapcar (lambda (ht) `(gethash ,key ,ht))
(nreverse hash-tables-list)))))
;; let's see its macroexpansion:
(macroexpand-1 '(mget 'a (*h3* *h2* *h1*)))
;; (GETHASH 'A *H3* (GETHASH 'A *H2* (GETHASH 'A *H1*))) ;
;; T
;; and run the code:
(mget 'a (*h2* *h1*))
;; 1 ;
;; NIL
One could attach information which are the next hash table to look in
in the hash-table object. And even automate the generation of the list (*h3* *h2* *h1*) so that one writes only
(gethash* key ht) which then calls mget ...
Well, of course through all this the hash-access gets slowed.
It is a trade-off between copying entire hash-tables or pay the cost in performance at each call ...
automatic finding of hash-tables which are extended by *h3*
(setf (get '*h3* 'extendeds) '(*h2* *h1*))
(setf (get '*h2* 'extendeds) '(*h1*))
(defun collect-extendeds (hts)
(let ((res (loop for ht in hts
nconcing (get ht 'extendeds))))
(remove-duplicates res)))
;; this function can recursively retrieve all hashtables
(defun get-extendeds* (hts &optional (acc '()))
(let ((hts (if (listp hts) hts (list hts))))
(let ((nexts (collect-extendeds hts)))
(cond ((every #'null nexts) (nreverse (remove-duplicates (append hts acc))))
(t (get-extendeds* nexts (remove-duplicates (append hts acc))))))))
;; write a macro to retrieve key's value from all downstream hashtables
(defmacro geth (key ht)
`(mget ,key ,(get-extendeds* ht)))
(geth 'a *h3*)
;; 1 ;
;; NIL ;; NIL because it was not in *h3* directly but in one of the hashtables
;; which it extends.
;; problem is if 'NIL is a value of an existing key,
;; one would still get 'NIL NIL.

How to create (d . nil) in lisp

I am beginner in Lisp. There is a question that I cannot solve.
Show the simplest expression that Lisp will print out when you type the following
expression:
’(a b (c . e) (d . nil))
I tried (cons 'a (cons 'b (cons (cons 'c 'e) (cons (cons 'd nil)))))
However, it wouldn't create (d . nil).
So, is there any way to create such a dot pair?
Show the simplest expression that Lisp will print out when you type the following expression
The question does not ask you to create a dotted pair, but how the dotted pair is printed. I suppose this question aims at showing you that lists are just dotted pairs printed in a special way.
If you write this in a REPL:
'(a b (c . e) (d . nil))
The REPL replies:
(A B (C . E) (D))
You have the exact same output if you write:
'(a . (b . ((c . e) . ((d . nil) . nil))))
Basically, (a b) is the list made of a symbol a in front of a sublist (b), written (a . (b)). Here I am only talking about how forms are read. If you write them directly in the REPL, they will first be read, then evaluated, and in that case you will get an error. That's why in the examples are quoted: an expression being quoted is not evaluated, but returned as-is.
If you want to write code that, when evaluated, produces the cons-cell (0 . 1), you have to write this list:
(cons 0 1)
The above is a list of three elements, which could be written equally:
(cons . (0 . (1 . nil)))
Your interpreter will read this list (a symbol, two numbers) and evaluate the resulting form. That forms produces, at runtime, the following cons cell:
(0 . 1)

Is assoc just syntax sugar for find?

CL-USER> *mylist*
((RED . 5) (RED . 4) (RED . 3) (BLUE . 5) (RED . 2) (BLUE . 4))
CL-USER> (assoc 'blue *mylist*)
(BLUE . 5)
CL-USER> (find 'blue *mylist* :key #'car)
(BLUE . 5)
It would seem to me that assoc is just a particular case of find, is this true or am I missing some extra functionality from assoc that is not apparent here?
From clhs.lisp.se/Body/f_assocc.htm:
The two expressions
(assoc item list :test fn)
and
(find item list :test fn :key #'car)
are equivalent in meaning with one exception: if nil appears in alist in place of a pair, and item is nil, find will compute the car of the nil in alist, find that it is equal to item, and return nil, whereas assoc will ignore the nil in alist and continue to search for an actual cons whose car is nil.

How do I convert a string to a symbol for use as a key in the Lisp "assoc" function?

I have this association-list in Common Lisp:
(defvar base-list (list (cons 'a 0) (cons 2 'c)))
I have to call assoc when my argument is of type string.
For the pair (A . 0) I have to convert "a" to a symbol, and for the pair (2 . C) I have to convert "2" to a symbol. How can I do that?
This should work like this:
CL-USER 28 : 1 > (assoc (convert-string-to-symbol "a") base-list)
(A . 0)
CL-USER 28 : 1 > (assoc (convert-number-to-symbol "2") base-list)
(2 . C)
I tried using intern but got NIL:
CL-USER 29 : 1 > (assoc (intern "a") base-list)
NIL
The function you want is called read-from-string:
CL-USER> (read-from-string "a")
A
1
CL-USER> (read-from-string "2")
2
1
CL-USER>
Note that solutions based on using intern or find-symbol would not work for strings representing numbers (e.g., "2") on most implementations.
You were close with intern; you just had the case wrong. Try this:
> (assoc (intern "A") base-list)
(A . 0)
Note that here the name-as-string is capitalized.
Alternately, you could use find-symbol to look for an existing symbol by name:
> (assoc (find-symbol "A") base-list)
(A . 0)
The key here is that when you wrote your original defvar form, the reader read the string "a" and—by virtue of the current readtable case—converted the symbol name to be uppercase. Symbols with names of different case are not equal. It just so happens that at read time the reader is projecting what you wrote (lowercase) to something else (uppercase).
You can inspect the current case conversion policy for the current reader using the readtable-case function:
> (readtable-case *readtable*)
:UPCASE
To learn more about how the readtable case and the reader interact, see the discussion in section 23.1.2 of the Hyperspec.

Print keys from plist based on values?

How do i iterate and print the keys of a plist based on given values?
Example:
; plist
(defun my-list() (list :a "hi" :b "no" :c "go"))
; from that list i want to iterate and print out keys based on values like:
for each x in ("hi" "go") print x
; hoping for:
ac
Im new to lisp - thank you :-)
Something like
(loop for (key value) on my-list by #'cddr
when (member value '("hi" "go") :test #'equal)
do (princ key))
The first line moves a pattern over the list.
you can use loop macro:
(loop
for (key value . rest) on list
by #'cddr
when (find value '("foo" "bar") :test #'string=)
do (princ key))