How to get a property from a plist

2019-07-20 18:44发布

问题:

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)

回答1:

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


回答2:

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