Can I use the function "get" with an assoc list? - lisp

I have the following code:
(setq months '((january 1) (february 2) ...))
Can I use get to use this list as a map (with keys and values)
Like:
(get 'months 'january)
and get the result: 1
Is it possible to do this in Lisp without explicitly setting the properties like this:
(setf (get 'months 'january) 1)
(setf (get 'months 'february) 2)
...
If this is not possible in Common Lisp, is it possible in any other dialect of Lisp?

No. The documentation for get says that the usage is
get symbol indicator &optional default => value
get finds a property on the property list of symbol whose property
indicator is identical to indicator, and returns its corresponding
property value. If there are multiple properties1 with that property
indicator, get uses the first such property. If there is no property
with that property indicator, default is returned.
A property list is defined as:
property list n. 1. a list containing an even number of elements that
are alternating names (sometimes called indicators or keys) and values
(sometimes called properties). When there is more than one name and
value pair with the identical name in a property list, the first such
pair determines the property. 2. (of a symbol) the component of the
symbol containing a property list.
Your data is in the form of an association list:
association list n. a list of conses representing an association of
keys with values, where the car of each cons is the key and the cdr is
the value associated with that key.
As such, you'd access values in it with assoc. You could work with a property list, in which case you'd either use getf if it's not a symbol's property list, and get (as described above) if it is a symbol's property list. Here are examples of all three approaches.
Association List
CL-USER> (defparameter *months-alist*
'((january . 1)
(february . 2)
(march . 3)))
*MONTHS-ALIST*
CL-USER> (cdr (assoc 'february *months-alist*))
2
Property List
CL-USER> (defparameter *months-plist*
'(january 1 february 2 march 3))
*MONTHS-PLIST*
CL-USER> (getf *months-plist* 'march)
3
Property List of a Symbol (uncommon)
CL-USER> (setf (get '*months* 'january) 1
(get '*months* 'february) 2
(get '*months* 'march) 3)
3
CL-USER> (get '*months* 'january)
1
Other Ways
Of course, you could also use a hash map, or in the case of this data where things are ordered, you could find the position of an element:
Hash Tables
CL-USER> (defparameter *months-hash* (make-hash-table :test 'eq))
*MONTHS-HASH*
CL-USER> (setf (gethash 'january *months-hash*) 1
(gethash 'february *months-hash*) 2
(gethash 'march *months-hash*) 3)
3
CL-USER> (gethash 'february *months-hash*)
2 ; the value
T ; it was present in the table
Position
CL-USER> (defparameter *months-sequence*
#(january february march april may june july august september))
*MONTHS-SEQUENCE*
CL-USER> (1+ (position 'april *months-sequence*))
4

You are using a-list as a data structure to implement associative maps. It has O(n) complexity (linear), so is not a good structure for large maps.
Common Lisp has a rich library with containers see its hyperspec, notably hash-tables. They might be much more relevant. You could still use a-lists if you believe they are very short (e.g. with the ASSOC Common Lisp function). You could also consider B-trees...

Related

defstruct - :read-only is not read only

In the CLHS I read for :read-only x: "When x is true, this specifies that this slot cannot be altered; it will always contain the value supplied at construction time."
Bit I can do this (CCL, SBCL):
CL-USER> (defstruct foo
(one 0 :read-only t))
FOO
CL-USER> (defparameter *foo* (make-foo))
*FOO*
CL-USER> *foo*
#S(FOO :ONE 0)
CL-USER> (setf (slot-value *foo* 'one) 1)
1 (1 bit, #x1, #o1, #b1)
CL-USER> *foo*
#S(FOO :ONE 1)
Shouldn't changing this slot be prohibited by Lisp?
slot-value is not how you access the fields of an object whose class was defined with defstruct. Such objects do not portably have named slots at all: they have named accessors.
Some implementations give the fields of such objects names, and may also allow access to them with slot-value: such behaviour is entirely nonportable however.
If you work within the language defined by the standard then you should not be able to modify the value of a structure field defined with a :read-only option.
The specification says:
setf will not accept the reader function for this slot.
slot-value is not the reader function created by defstruct. The reader function is foo-one (unless you override the naming scheme with the :conc-name keyword). So you should get an error if you try to do
(setf (foo-one *foo) 1)

Why does mapcar in lisp take a name instead of function?

I am going through a lisp book and I am looking at mapcar, my question is why is that this is valid:
> (mapcar #'+ '(1 2) '(3 4))
but this one isn't:
(mapcar + '(1 2) '(3 4))
in other words, is there a reason it was decided in lisp that the first argument to mapcar cannot be the function itself, and has to be its name? what purpose does this serve?
is there a reason it was decided in lisp that the first argument to mapcar cannot be the function itself, and has to be its name? what purpose does this serve?
It's just that in something like Common Lisp, the identifier + has multiple different purposes. It is a variable, a function, plus various other things.
writing + means the variable. It is used by the read eval print loop. The value of + is the last form that was evaluated, the value of ++ is the previous value of +, and the value of +++ is the previous value of ++.
To tell Common Lisp that you want to use the function value of an identifier +, one has to write (function +) or shorter #'+.
Thus
(mapcar (function +) '(1 2) '(3 4))
or shorter
(mapcar #'+ '(1 2) '(3 4))
actually means call mapcar with the function + and the lists (1 2) and (3 4)
There are two other ways to use the function +.
(mapcar '+ '(1 2) '(3 4))
Above will have Lisp retrieve the global function value of the symbol +.
Fourth, we can also have the function object be a part of the source code.
#'+ is a function. Common Lisp is what's called a 'lisp 2', which means that it has two namespaces: during evaluation of a compound form, like (+ a b), the function position is looked up in the function namespace while the other positions are looked up in the value namespace. This means that, for instance (append list list) does not make a list whose two elements are the list function: it makes a list whose two elements are whatever list happens to be bound to as a value.
But sometimes you need to get the function value of a name in a value position: an example is in the first argument of mapcar. To do that there is a special operator, function: (mapcar (function +) x y) adds the elements of two lists. Like quote, function has a read macro, which is #'.
(To make this more fun, mapcar actually expects a function designator so you can use the nsme of a function: (mapcar '+ x y) will work, although it is a bit horrid.)

Can I use assoc when the keys are strings?

I have a data set like this: '(("red" 3 5)("blue" 6 8)...)
Is it possible to use assoc when the keys are strings? None of the obvious attempts have worked for me in this simple test:
CL-USER> (defparameter ggg (list '("foot" 2) '(bar 5)))
GGG
CL-USER> ggg
(("foot" 2) (BAR 5))
CL-USER> (assoc 'bar ggg)
(BAR 5)
CL-USER> (assoc "foot" ggg)
NIL
CL-USER> (assoc '"foot" ggg)
NIL
CL-USER> (assoc 'foot ggg)
NIL
If you are sure that your list contains only strings, you can use the type-specific functions string= (case sensitive) or string-equal (case insensitive).
However, these functions also accept symbols, and mixtures of symbols and strings.
Thus (assoc "ABC" list :test #'string=) will find not only the key "ABC" but also any symbol whose name is "ABC", such as the symbol :abc or cl-use:abc or mypackage:abc.
The generic equal and equalp functions for comparing any two objects do not have this behavior. Like the aforementioned two, equal and equalp are, respectively, case sensitive and insensitive. However, they also compare other kinds of objects.
Unlike string= and string-equal, equal and equalp do not consider strings and symbols to be equivalent; that is, (equalp "FOO" 'FOO) -> nil. They also do not consider symbols having the same name to be equivalent: (equalp 'foo :foo) -> nil. When both arguments are symbols, equal and equalp apply the same test as the eq function.
So I would argue that an appropriate test for your associative list, since you have a mixture of string and symbolic keys, is one of the two functions equal and equalp.
These functions will also allow your list to have other kinds of keys like numbers. equalp will compare numbers by value, so that 1 and 1.0 are the same key, whereas equal is tighter. Both these functions recurse into lists. The lists (1 2) and (1 2) are equal even if they are not the same object (separately consed), whereas (1 2) and (1 2.0) are not equal, but are equalp (unless you have a very weird floating-point system). Also vector objects are not compared element-by-element by equal, but they are by equalp.
Even if you had strings only in the list, it's still better to use these two functions.
You are not going to get much, if any, performance benefit. string= still has to validate the types of the arguments to make sure they are supported types, and dispatch according to what combination of string and symbol the arguments are. equal dispatches according to numerous type possibilities, but this can be done efficiently.
Using overly type-specific functions, or inappropriately strict equality, as a matter of habit, is a poor practice in Lisp.
string= is used deliberately, however, not for saving machine cycles, but in situations when symbols must be compared as strings, or mixtures of symbols and strings. For instance, if you were implementing the loop macro, you might use string= for detecting the loop clause words, which according to the ANSI Common Lisp spec are treated as equivalent based on symbol name. Users can write (loop :for x below 42 ...) or (loop mypackage:for x below 42 ...). However (loop "FOR" ...) is not valid! So you could not rely only on string=; you'd have to validate that the clause word is a symbol.
(assoc "foot" ggg :test #'string-equal)
or
(assoc "foot" ggg :test #'string=)
Depending on whether you want the comparison to be case-sensitive.
? (assoc "foot" ggg :test #'equalp)
("foot" 2)
As others have pointed out already you can use different kinds of keys as described in the HyperSpec:
(setq alist '(("one" . 1)("two" . 2))) => (("one" . 1) ("two" . 2))
(assoc "one" alist) => NIL
(assoc "one" alist :test #'equalp) => ("one" . 1)

Elisp: How to delete an element from an association list with string key

Now this works just fine:
(setq al '((a . "1") (b . "2")))
(assq-delete-all 'a al)
But I'm using strings as keys in my app:
(setq al '(("a" . "foo") ("b" . "bar")))
And this fails to do anything:
(assq-delete-all "a" al)
I think that's because the string object instance is different (?)
So how should I delete an element with a string key from an association list? Or should I give up and use symbols as keys instead, and convert them to strings when needed?
If you know there can only be a single matching entry in your list, you can also use the following form:
(setq al (delq (assoc <string> al) al)
Notice that the setq (which was missing from your sample code) is very important for `delete' operations on lists, otherwise the operation fails when the deleted element happens to be the first on the list.
The q in assq traditionally means eq equality is used for the objects.
In other words, assq is an eq flavored assoc.
Strings don't follow eq equality. Two strings which are equivalent character sequences might not be eq. The assoc in Emacs Lisp uses equal equality which works with strings.
So what you need here is an assoc-delete-all for your equal-based association list, but that function doesn't exist.
All I can find when I search for assoc-delete-all is this mailing list thread:
http://lists.gnu.org/archive/html/emacs-devel/2005-07/msg00169.html
Roll your own. It's fairly trivial: you march down the list, and collect all those entries into a new list whose car does not match the given key under equal.
One useful thing to look at might be the Common Lisp compatibility library. http://www.gnu.org/software/emacs/manual/html_node/cl/index.html
There are some useful functions there, like remove*, with which you can delete from a list with a custom predicate function for testing the elements. With that you can do something like this:
;; remove "a" from al, using equal as the test, applied to the car of each element
(setq al (remove* "a" al :test 'equal :key 'car))
The destructive variant is delete*.
Emacs 27+ includes assoc-delete-all which will work for string keys, and can also be used with arbitrary test functions.
(assoc-delete-all KEY ALIST &optional TEST)
Delete from ALIST all elements whose car is KEY.
Compare keys with TEST. Defaults to ‘equal’.
Return the modified alist.
Elements of ALIST that are not conses are ignored.
e.g.:
(setf ALIST (assoc-delete-all KEY ALIST))
In earlier versions of Emacs, cl-delete provides an alternative:
(setf ALIST (cl-delete KEY ALIST :key #'car :test #'equal))
Which equivalently says to delete items from ALIST where the car of the list item is equal to KEY.
n.b. The answer by Kaz mentions this latter option already, but using the older (require 'cl) names of delete* and remove*, whereas you would now (for supporting Emacs 24+) use cl-delete or cl-remove (which are auto-loaded).
If using emacs 25 or newer you can use alist-get
(setf (alist-get "a" al t t 'equal) t)

Why do we need `nil`?

I do not see why we need nil [1] when to cons a sequence (so-called proper list) of items. It seems to me we can achieve the same goal by using the so-called improper list (cons-ed pairs without an ending nil) alone. Since Lisps [2] have already provided a primitive procedure to distinguish between a pair? and an atom (some implementations even provide atom?), when defining a procedure on a list, e.g., length, I can do the same with just dotted-pairs, as shown below:
(define len
(lambda (l)
(cond ((pair? l) (+ 1 (len (cdr l))))
(else 1) ) ) )
It is obvious that we can apply this procedure to an improper list like '(1 . (2 . 3)) to get the expected answer 3, in contrast to the traditional (length '(1 2 3)).
I'd like to hear any opinions in defense of the necessity of nil. Thanks in advance.
[1] Let's ignore the debate among nil/NIL, '() and ().
[2] Here it means the Lisp family of languages.
Working with lists without nil (or '()) would be like doing arithmetic without zero. Using only pairs without nil, how would we represent an empty list, or a singleton list '(1)?
It gets worse: since lists don't have to be lists of atoms, but can contain other lists, how would we represent the nested list '(1 2 (3 4))? If we do the following conversions:
'(3 4) => '(3 . 4)
'(1 2 x) => '(1 . (2 . x)) == '(1 2 . x)
we get:
'(1 2 (3 4)) => '(1 . (2 . (3 . 4))) == '(1 2 3 . 4)
But also:
'(1 2 3 4) => '(1 . (2 . (3 . 4))) == '(1 2 3 . 4)
So constructing lists only using pairs and no nil prevents us from distinguishing between a nested list structure and a flat list, at least at the end of the list. You can still include nested lists as any element except the last, so now there's a strange and arbitrary limitation on what the elements of a list can be.
More theoretically, proper lists are an inductively defined data type: a list is either the empty list, or it has a first element, which can be anything, and a rest, which is always another list defined in the same way. Take away the empty list, and now you have a data type where the rest might be another list, or it might be the last element of the list. We can't tell except by passing it to pair?, which leads to the problem with nested listing above. Keeping nil around lets us have whatever we like as list elements, and allows us to distinguish between 1, '(1), '((1)) and so on.
You need it to represent "Nothing".