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))
Related
I am a newbie in Lisp.
I want to access a particular property from a property list with a string variable like this
(setf sym (list :p1 1))
(setf x "p1")
(getf sym :x)
About cl:getf
Let Petit Prince's answer is right that getf is probably the function you want to use here, but note that it can be used for more than just keyword symbols. You can use it for any objects. A property list is just a list of alternating indicators and values, and any object can be an indicator:
(let ((plist (list 'a 'b 'c 'd)))
(getf plist 'c))
;=> D
You can even use strings as indicators:
(let* ((name "p1")
(plist (list name 1)))
(getf plist name))
;=> 1
However, that's probably not great practice, since getf compares indicators with eq. That means that using strings as indicators might not be reliable, depending on your use case:
(let ((plist (list "p1" 1)))
(getf plist "p1"))
;=> NIL
For your example
In your case, you're trying to take a string and find the object for a symbol with a name that's string-equal (i.e., with the same characters, but disregarding case). It probably makes more sense to loop over the list and compare indicators with string-equal.
(let ((plist '(:p1 1 :p2 2)))
(loop
for (indicator value) on plist by #'cddr
when (string-equal indicator "p1")
return value))
;=> 1
And of course, you can wrap that up in a function for abstraction:
(defun getf-string-equal (plist indicator)
(loop
for (i v) on plist by #'cddr
when (string-equal i indicator)
return v))
(getf-string-equal '(:p1 1 :p2 2) "p1")
;=> 1
The second parameter to getf is a keyword, and you have string. A keyword is a symbol that lives in the package KEYWORD and has usually been uppercased by the reader:
? (setf sym (list :p1 1))
(:P1 1)
? sym
(:P1 1)
So you need to use:
? (getf sym (find-symbol (string-upcase x) "KEYWORD"))
1
I wrote a function to convert alist to hash:
(defun hash-alist (alist)
"Convert association list to a hash table and return it."
(let ((my-hash (make-hash-table :test 'equal)))
(dolist (entry alist)
(if (gethash (car entry) my-hash)
(error "repeated key"))
(puthash (car entry) (cdr entry) my-hash))
my-hash))
but when I run it as following, why I get nil at the end?
Run:
(setq a '(("a" . 2) ("b" . 1)))
(setq b (hash-alist a))
(maphash (lambda (x y) (princ (format "%s:%d " x y) t))
b)
Output:
a:2 b:1 nil
nil is the return value of maphash. Nothing more than that.
It is the way that you are evaluating the maphash sexp that causes the return value to be printed.
If you look in buffer *Messages* you might see something like this (depending on how you evaluate the expression):
Evaluating...
a:2 b:1
Buffer `*Pp Eval Output*' is in mode `Emacs-Lisp'. For info on the mode: `C-h m'.
nil
The return value is documented in the Elisp manual, node Hash Access. It should also be, but is not, documented in the doc string.
Every Lisp expression has a value.
c-x c-e evaluates an expression and prints the result.
If you evaluate (+ 1 2) you see that it evaluates to 3. If you evaluate a maphash expression, then it evaluates to NIL. So this is printed.
Since your code calls functions which produce output, you see that output printed before the return value. So there is no extra NIL. It is just the NIL that is the result.
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
Now i have to copy the hastable to a list before sorting it:
(defun good-red ()
(let ((tab (make-hash-table)) (res '()))
(dotimes (i 33) (setf (gethash (+ i 1) tab) 0))
(with-open-file (stream "test.txt")
(loop for line = (read-line stream nil)
until (null line)
do
(setq nums (butlast (str2lst (substring line 6))))
(dolist (n nums) (incf (gethash n tab)))
))
**(maphash #'(lambda (k v) (push (cons k v) res)) tab)**
(setq sort-res (sort res #'< :key #'cdr))
(reverse (nthcdr (- 33 18) (mapcar #'car sort-res))) ))
BTW, what's the better way to fetch the first N elements of a list ?
Vatine's answer is technically correct, but probably not super helpful for the immediate problem of someone asking this question. The common case of using a hash table to hold a collection of counters, then selecting the top N items by score can be done like this:
;; convert the hash table into an association list
(defun hash-table-alist (table)
"Returns an association list containing the keys and values of hash table TABLE."
(let ((alist nil))
(maphash (lambda (k v)
(push (cons k v) alist))
table)
alist))
(defun hash-table-top-n-values (table n)
"Returns the top N entries from hash table TABLE. Values are expected to be numeric."
(subseq (sort (hash-table-alist table) #'> :key #'cdr) 0 n))
The first function returns the contents of a hash table as a series of cons'd pairs in a list, which is called an association list (the typical list representation for key/value pairs). Most Lisp enthusiasts already have a variation of this function on hand because it's such a common operation. This version is from the Alexandria library, which is very widely used in the CL community.
The second function uses SUBSEQ to grab the first N items from the list returned by sorting the alist returned by the first function using the CDR of each pair as the key. Changing :key to #'car would sort by hash keys, changing #'> to #'< would invert the sort order.
A hash-table is inherently unordered. If you want it sorted, you need to initialize some sort of ordered data structure with the contents.
If you want to fetch the first N elements of a sequence, there's always SUBSEQ.
I'm just learning LISP and i am having trouble doing the following:
; return ":h :i"
(defun get-char()
(loop for char across "ab"
collect (concatenate 'string ":" (string char))))
; plist
(defun get-list() (list :a "1" :b "2"))
; I cannot get this to work
; <-- returns all null, cannot get plist values :-(
(loop for x in (get-char)
collect (getf (get-list) x))
; this works fine...
(loop for x in '(:a :b)
collect (getf (get-list) x))
I know im close, but i am just missing something.
Thanks a lot :-)
Change the get-char function to return a list of keywords from the characters:
(defun get-char()
(loop
for char across "ab"
collect (intern (string-upcase char) :keyword)))
Evaluating (get-char) => (:A :B). Furthermore:
(loop for x in (get-char) collect (getf (get-list) x))
=>
("1" "2")