How can automatically identify or allow the user of my program to identify variables in Lisp? - lisp

I have something of a subtle (I think) problem in Lisp, related to an earlier question I asked: How do I name a variable after a string in Lisp?
Here's how it goes:
I'm creating a language in which (among other things) the user inputs text strings, and my parser then creates uniquely-identified entity (and other stuff) based on their input.
A typical and simple input would be: my_name:my_contents -- where my_name is the name of the entity, and my_contents is a string.
So, I need to find a way to set up a lisp program that takes the user input, then creates the entities and identifies them based on the user's input.
So far I've considered two approaches:
A. One variable per entity: when the user enters a string, Lisp takes my_name and creates a variable with that name, and puts my_contents inside. The drawback here is that I can't figure out how to set the name of the variable to some user-defined and mutable input such as my_name.
B. A table, such as a hash-table: when the user enters a string, Lisp adds a row to a hash table with my_name as the key and my_contents as the entry. The drawback with this approach is that again I can't find a way to set the key as my_name at any given moment.
In summary, I'm looking for counsel on 1. how to name a variable after a user-defined string or 2. how to set a user-defined string as a key in a hash table or 3. whether there's a better approach.
Thanks in advance!

If you're feeling confused, I suggest you breakdown your plan into smaller pieces. Or just use the repl to play with the ideas, that way you'll see many of your questions answered.
Start: create a hash table:
>> (make-hash-table)
;;... some hash table is created ...
N.B. below, you'll see that this type of hash table won't work with your string keys, but keep going:
wait, you need to refer to it, let's set it to a variable:
>> (setf my-hash *)
or similar:
>> (setf my-hash (make-hash-table))
add some values:
>> (setf (gethash "key1" my-hash) "value1")
>> (setf (gethash "key2" my-hash) "value2")
hmm, I'd like to see the values. There are a few options. Let's check CLHS for one of them: http://clhs.lisp.se/Body/f_maphas.htm#maphash
Copy-paste the last line of the example:
>> (maphash #'(lambda (k v) (print (list k v))) my-hash)
("key1" "value1")
("key2" "value2")
NIL
Now I wonder if I can use gethash to reach entries:
>> (gethash "key1" my-hash)
NIL
I can't! That's because make-hash-table we used above defaults to using 'eql as the test predicate. We need to change that. Do everything again with a better suited hash table:
>> (setf my-hash (make-hash-table :test 'equal))
>> (setf (gethash "key1" my-hash) "value1")
>> (setf (gethash "key2" my-hash) "value2")
>> (maphash #'(lambda (k v) (print (list k v))) my-hash)
("key1" "value1")
("key2" "value2")
>> (gethash "key1" my-hash)
"value1"
that's better.
Forgive me for starting from basics before coming to your question. I felt like we sometimes forget the principles / basics. If you play with your repl like this, I guess you'll find that it'll be easier to find some of the answers you're looking for.
And now, to add to Numbra's answer, here is some pointers:
if you want to use hash tables:
>> (setf user-entered-string "VARIABLE1")
>> (setf user-entered-value "value1")
>> (setf (gethash user-entered-string) user-entered-value)
or, symbols:
>> (intern user-entered-string)
;; now it is a symbol object in current package:
>> (find-symbol user-entered-string)
>> (setf (symbol-value (find-symbol user-entered-string)) user-entered-value)

I'm not sure to understand all of your problems, but:
Name a variable after a user-defined string:
The function that (more or less) does that is intern (or read, but that is way more general). You probably want to intern things in a "user package", so as not to conflict with the actual code.
Hash-tables
Here, I am not sure to really understand what it is that causes you troubles: if the user enters a string (that you can get with e.g. read-line or something more complex if you wish), bound in your code to, say, user-string-name, you can just do (setf (gethash user-string-variable *user-defined-variables*) user-string-value), assuming you have created such a hash-table. Note that by default, hash-tables use eql to compare keys, which is not what you want for strings; you'll need to create the table with the argument :test 'equal.
Other option
Using symbol property lists.
There are probably even more solutions that I haven't thought of. Deciding which of those is the best probably depends on many things, and I'll let you decide, given your application, which one you prefer.

Related

Presenting a list

I have this list of names and different languages
(setq l '((david spanish german)
(amanda italian spanish english)
(tom german french)))
I want to do the next with a function: for each language, I need every name relationed with every language.
For example, if I call the function with the list L:
(lenguages L)
I want to show this:
( (english (amanda))
(spanish (david amanda))
(italian (amanda))
(german(david tom))
(french(tom))
)
I have an idea of how to do this, but it shows just one item.
(defun lenguages(names)
(cond((null names) nil)
((list (cadar names) (list (caar names))))))
this last function only show (spanish (david))
An iteration-based task like this is best suited to Common Lisp's immensely powerful loop macro. You can read all the details about this macro in the GigaMonkeys book, but we'll just go over the parts you need for this problem here. Let's start with the function definition.
(defun lenguages (names)
...)
Inside this, we want to iterate over the provided list. We also want to collect some keys, so a hash table would be useful to have. Hash tables (called maps or dicts in many other languages) associate keys to values in a time-efficient way.
(loop with hash = (make-hash-table)
for entry in names
for name = (car entry)
do ...
finally ...)
The loop macro is very powerful and has a language all its own. The with clause declares a local variable, in this case a hash table. The first for defines an iteration variable. The loop will run with entry bound to each entry of names and will stop when it runs out of entries. The third line is another local variable, but unlike with, a for variable is rebound every time, so at each iteration name will be the first element of entry. The do block contains arbitrary Lisp code that will be executed each iteration, and finally contains a block of Lisp code to execute at the end of the loop.
Inside the do block, we want to add the person's name to the hash table entry for each language they know, so we need another loop to loop over the known languages.
(loop for lang in (cdr entry)
do (push name (gethash lang hash)))
This loop goes inside the do block of the outer one. For each language in the person's list of known languages, we want to prepend that person's name onto the hash value for that language. Normally, we would have to consider the case in which the hash key doesn't exist, but luckily for us Common Lisp defaults to nil if the hash key doesn't exist, and prepending an element to nil creates a one-element list, which is just what we want.
Now, when this loop is done, the hash table will contain all the languages and keys and lists of people who know them as values. This is the data that you want, but it's not in the format you want. In fact, if we put this in our finally block
(return hash)
We would get some semi-useful output* that tells us we're on the right track.
#S(HASH-TABLE :TEST FASTHASH-EQL ((TOM GERMAN FRENCH) . (TOM TOM))
((AMANDA ITALIAN SPANISH ENGLISH) . (AMANDA AMANDA AMANDA))
((DAVID SPANISH GERMAN) . (DAVID DAVID)))
Instead, let's do one more loop to convert this hash table to the list that you want it to be. Here's what we want in the finally block now.
(return (loop for key being the hash-keys of hash using (hash-value value)
collect (list key value)))
This uses the relatively obscure being syntax for the loop macro, which allows easy iteration over hash tables. You should read this as: for every key-value pair, collect a list containing the key followed by the value into a list, then return the accumulated list. This is yet another of the loop macros interesting features: it tries to provide primitives for common use cases such as accumulating values into a list. And it comes in handy in cases like this.
Here's the complete code block.
(defun lenguages (names)
(loop with hash = (make-hash-table)
for entry in names
for name = (car entry)
do (loop for lang in (cdr entry)
do (push name (gethash lang hash)))
finally (return (loop for key being the hash-keys of hash using (hash-value value)
collect (list key value)))))
That link I provided earlier is to the GigaMonkeys book on Common Lisp, which is available online for free. I strongly encourage reading through it, as it's an amazing reference for all things Common Lisp. Especially if you're just starting out, that book can really set you in the right direction.
* Your output format may vary on this. The implementation chooses how to output structs.
The other answer is fine: here is a version which does not use loop or intermediate hashtables, but instead builds the required association-list directly. It's worth comparing the efficiency of this against the hash-table-based one: it does a lot more searching down lists, but in practice, for small amounts of data such things are often faster (hashtables have nontrivial overhead in many implementations) and it will always use less storage as it builds no structure it does not return.
Note that:
this will return results in a different order in general (it has no dependency on hash ordering);
this will ensure there is only one occurrence of each person for each language: (languages '((david german german))) is ((german (david))) not ((german (david david))) -- it pays some performance cost (which could be ameliorated for large data using more hashtables) for doing this.
So, here it is:
(defun languages (people)
(let ((langs '())) ;the map we are building
(dolist (pl people langs)
(destructuring-bind (person . person-languages) pl
(dolist (lang person-languages)
(let ((entry (assoc lang langs)))
(if (not (null entry))
;; there's an entry for lang: add the person to it
(pushnew person (second entry))
;; there is no entry, create one with person in it
(setf langs `((,lang (,person)) ,#langs)))))))))
(Note also that the loop based version could use loop's destructuring, which might be a little clearer.)

Can CONS objects be used as a key to a hash table?

I've been playing with LISP lately, and I want to attempted to optimize a terribly inefficient recursive function by caching it's output so it only runs each combination of parameters once. I want to save the results into hash table, using either (LIST a b) or (CONS a b) as the key. My question is, is that even possible? Documentation I've read would lead me to believe it is, as the document I read defines the key as an 'object', and defines 'object' as something that was constructed with cons. So I gave this a try.
(defparameter *table* (make-hash-table))
(setf (gethash (list 1 2) *table*) 123)
(format t "~S~%" (gethash (list 1 2) *table*))
And it gives me NIL, when I would expect 123. If i replace the (list 1 2) with a cons it still doesn't work, but when I use an plain integer, it works fine.
I'm using GCL 2.6.12
(make-hash-table :test #'equal)
Solutions for caching results of functions has been already implemented for Common Lisp. One is library fare-memoization, the second is function-cache and both are accessible with the Quicklisp. Using the former is as easy as defining normal function:
(define-memo-function your-time-consuming-function (arg)
(code arg))
For reference please check https://github.com/fare/fare-memoization.

Merging symbols in common lisp

I want to insert a char into a list. However, I want to merge this char with the last symbol in the list. With appends and cons the result is always two different symbols. Well, I want one merged symbol to be my result.
Example:
(XXXX 'a '5) ====> (a5)
What I would like to have, instead of:
(XXXX 'a '5) ====> (a 5)
You cannot "merge symbols" in Lisp.
First of all, 5 is not a symbol, but a number. If you want a symbol named "5" you have to type it as |5| (for example).
If a function takes the symbol A and symbol |5|, and produces the symbol A5, it has not merged symbols. It has created a new symbol whose name is the catenation of the names of those input symbols.
Properly designed Lisp programs rarely depend on how a symbol is named. They depend on symbols being unique entities.
If you're using symbols to identify things, and both 5 and A identify some entity, the best answer isn't necessarily to create a new symbol which is, in name at least, is a mashup of these two symbols. For instance, a better design might be to accept that names are multi-faceted or compound in some way. Perhaps the list (A 5) can serve as a name.
Common Lisp functions themselves can have compound names. For instance (setf foo) is a function name. Aggregates like lists can be names.
If you simply need the machine to invent unique symbols at run-time, consider using the gensym function. You can pass your own prefix to it:
(gensym "FOO") -> #:FOO0042
Of course, the prefix can be the name of some existing symbol, pulled out via symbol-name. The symbol #:FOO0042 is not unique because of the 0042 but because it is a freshly allocated object in the address space. The #: means it is not interned in any package. The name of the symbol is FOO0042.
If you still really want to, a simple way to take the printed representation of a bunch of input objects and turn it into a symbol is this:
(defun mashup-symbol (&rest objects)
(intern (format nil "~{~a~}" objects)))
Examples:
(mashup-symbol 1 2 3) -> |123|
(mashup-symbol '(a b) 'c 3) -> |(A B)C3|
Define this:
(defun symbol-append (&rest symbols)
(intern (apply #'concatenate 'string
(mapcar #'symbol-name symbols))))
and then use it as:
* (symbol-append 'a 'b 'c)
ABC
* (apply #'symbol-append '(a b c))
ABC
If you expect your arguments to contain stuff besides symbols, then replace symbol-name with:
(lambda (x)
(typecase x ...))
or a pre-existing CL function (that I've forgotten :() that stringifies anything.
The answer to the question you ask is
(defun concatenate-objects-to-symbol (&rest objects)
(intern (apply #'concatenate 'string (mapcar #'princ-to-string objects))))
(concatenate-objects 'a 'b) ==> ab
Oh, if you want a list as the result:
(defun xxxx (s1 s2) (list (concatenate-objects-to-symbol s1 s2)))
However, I am pretty sure this is not the question you actually want to ask.
Creating new symbols programmatically is not something beginners should be doing...

How to acquire unique object id in Emacs Lisp?

Does emacs lisp have a function that provides a unique object identifier, such as e.g. a memory address? Python has id(), which returns an integer guaranteed to be unique among presently existing objects. What about elisp?
The only reason I know for wanting a function like id() is to compare objects, and ensure that they only compare equal if they are the same (as in, in the same memory location). In Lisps, this is done a bit differently from in Python:
In most lisps, including elisp, there are several different notions of equality. The most expensive, and weakest equivalence is equal. This is not what you want, since two lists (say) are equal if they have the same elements (tested recursively with equal). As such
(equal (list 1 2) (list 1 2)) => T
is true. At the other end of the spectrum is eq, which tests "identity" rather than equality:
(eq (list 1 2) (list 1 2)) => NIL
This is what you want, I think.
So, it seems that Python works by providing one equality test, and then a function that gives you a memory location for each object, which then can be compared as integers. In Elisp (and at least Common Lisp too), on the other hand, there is more than one meaning of "equality".
Note, there is also "eql", which lies somewhere between the two.
(EDIT: My original answer probably wasn't clear enough about why the distinction between eq and equal probably solves the problem the original poster was having)
There is no such feature in Emacs Lisp, as far as I know. If you only need equality, use eq, which performs a pointer comparison behind the scenes.
If you need a printable unique identifier, use gensym from the cl package.
If you need a unique identifier to serve as an index in a data structure, use gensym (or maintain your own unique id — gensym is simpler and less error-prone).
Some languages bake a unique id into every object, but this has a cost: either every object needs extra memory to store the id, or the id is derived from the address of the object, which precludes modifying the address. Python chooses to pay the cost, Emacs chooses not to.
My whole point in asking the question was that I was looking for a way to distinguish between the printed representations of different symbols that have the same name. Thanks to the elisp manual, I've discovered the variable print-gensym, which, when non-nil, causes #: to be prepended to uninterned symbols printed. Moreover, if the same call to print prints the same uninterned symbol more than once, it will mark the first one with #N= and subsequent ones with `#N#. This is exactly the kind of functionality I was looking for. For example:
(setq print-gensym t)
==> t
(make-symbol "foo")
==> #:foo
(setq a (make-symbol "foo"))
==> #:foo
(cons a a)
==> (#1=#:foo . #1#)
(setq b (make-symbol "foo"))
==> #:foo
(cons a b)
==> (#:foo . #:foo)
The #: notation works for read as well:
(setq a '#:foo)
==> #:foo
(symbol-name a)
==> "foo"
Note the ' on '#:foo--the #: notation is a symbol-literal. Without the ', the uninterned symbol is evaluated:
(symbol-name '#:foo)
==> "foo"
(symbol-name #:foo)
==> (void-variable #:foo)

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)