Say I have a list with keywords:
'(("element1" :keyword1 "a" :keyword2 "b")
("element2" :keyword3 "c" :keyword4 "d")
("element3" :keyword2 "e" :keyword4 "f"))
Which functions can I use to find which list elements contain :keyword2 and find its value in each list? I'm trying to do this in Emacs Lisp but I think with the cl package I could possibly adapt a Common Lisp solution? I've tried to use the find function as illustrated here but to no avail (of course, after changing a few syntax elements to adapt the examples to Emacs Lisp).
(require 'cl)
(defvar *data* '(("element1" :keyword1 "a" :keyword2 "b")
("element2" :keyword3 "c" :keyword4 "d")
("element3" :keyword2 "e" :keyword4 "f")))
(find :keyword2 *data* :test #'find)
;;=> ("element1" :keyword1 "a" :keyword2 "b")
(getf (cdr (find :keyword2 *data* :test #'find)) :keyword2)
;;=> "b"
;; Above only finds the first match; to find all matches,
;; use REMOVE* to remove elements that do not contain the keyword:
(remove* :keyword2 *data* :test-not #'find)
;;=> (("element1" :keyword1 "a" :keyword2 "b")
;; ("element3" :keyword2 "e" :keyword4 "f"))
(mapcar (lambda (x) (getf (cdr x) :keyword2))
(remove* :keyword2 *data* :test-not #'find))
;;=> ("b" "e")
In Common Lisp, you would typically extract the values with destructuring-bind in this case, something like
(destructuring-bind (string &key keyword2 &allow-other-keys)
'("element1" :keyword1 "a" :keyword2 "b")
(list string keyword2)) ; or do anything with string and keyword2
should result in
("element1" "b")
In lisp, you would typically use a so-called associative list (or alist for short). It has the following form:
((key1 . value1) (key2 . value2) (key3 . value3))
There are a number of functions designed to work with alists, including assq and assoc, which return the dotted-pair, or nil.
Install dash list manipulation library (the GitHub link contains instructions). It contains multitude of helpful functions to achieve any goal. Suppose, your list above is named data, then you could:
(--find-indices (-elem-indices :keyword2 it) data) ; => (0 2)
(--map (cadr (--drop-while (not (eq :keyword2 it)) it)) data) ; => ("b" nil "e")
(--map-indexed (cons it-index ; => ((0 . "b") (1) (2 . "e"))
(cadr (--drop-while (not (eq :keyword2 it)) it))) data)
Related
I am trying to code a function that will let me input a list, and it will produce a list of lists. Each list in the product will contain i+1 duplicates of the what was in index i of the original list.
So something like (expand (list "a" "b" "c")) will give me (list (list "a") (list "b" "b") (list "c" "c" "c")).
I am using Racket Beginning Student with List Abbreviations, and I am not allowed to use the "make-list" function.
Write helper function with new argument (i = number of duplicates) and use function make-list for repeating given element i times (or write your own version of make-list):
(define (my-make-list i elem)
(if (<= i 0) '()
(cons elem (my-make-list (- i 1) elem))))
(define (expand-help lst i)
(if (null? lst) '()
(cons (my-make-list i (car lst))
(expand-help (cdr lst) (+ i 1)))))
(define (expand lst)
(expand-help lst 1))
Example:
> (expand (list "a" "b" "c"))
(list (list "a") (list "b" "b") (list "c" "c" "c"))
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.
I have a file named test.txt, it contains
"hello this is a test file"
I want to read it from the file so that every word represents lists of character and every paragraph represents lists of words means that I want to store them into a nested list like:
(list(list (h e l l o)) (list(t h i s))(list(i s)) (list(a)) (list(t e s t)) (list(f i l e))))
I am totally new in lisp and have a lot of confusion about this problem.
Solution without any dependencies
(defun split (l &key (separators '(#\Space #\Tab #\Newline)) (acc '()) (tmp '()))
(cond ((null l) (nreverse (if tmp (cons (nreverse tmp) acc) acc)))
((member (car l) separators)
(split (cdr l) :separators separators
:acc (if tmp (cons (nreverse tmp) acc) acc)
:tmp '()))
(t
(split (cdr l) :separators separators
:acc acc
:tmp (cons (car l) tmp)))))
(defun read-file-lines (file-path)
(with-open-file (f file-path :direction :input)
(loop for line = (read-line f nil)
while line
collect line)))
(defun read-file-to-word-characters (file-path)
(mapcan (lambda (s) (split (coerce s 'list)))
(read-file-lines file-path)))
(read-file-to-word-characters "~/test.lisp.txt")
;; ((#\h #\e #\l #\l #\o) (#\t #\h #\i #\s) (#\i #\s) (#\a) (#\t #\e #\s #\t)
;; (#\f #\i #\l #\e))
Convert the characters to one-letter strings:
;; apply to elements of nested list (= a tree) the conversion function `string`
(defun map-tree (fn tree)
(cond ((null tree) '())
((atom tree) (funcall fn tree))
(t (mapcar (lambda (branch) (map-tree fn branch)) tree))))
(map-tree #'string (read-file-to-word-characters "~/test.lisp.txt"))
;; (("h" "e" "l" "l" "o") ("t" "h" "i" "s") ("i" "s") ("a") ("t" "e" "s" "t")
;; ("f" "i" "l" "e"))
Content of "~/test.lisp.txt":
hello this
is a test file
Solution using cl-ppcre (Edi Weitz's congenial regex package)
;; look here in an answer how to use cl-ppcre:split
;; https://stackoverflow.com/questions/15393797/lisp-splitting-input-into-separate-strings
(ql:quickload :cl-ppcre)
(defun read-file-lines (file-path)
(with-open-file (f file-path :direction :input)
(loop for line = (read-line f nil)
while line
collect line)))
(defun string-to-words (s) (cl-ppcre:split "\\s+" s))
(defun to-single-characters (s) (coerce s 'list))
(defun read-file-to-character-lists (file-path)
(mapcan (lambda (s)
(mapcar #'to-single-characters
(string-to-words s)))
(read-file-lines file-path)))
(read-file-to-character-lists "~/test.lisp.txt")
;; ((#\h #\e #\l #\l #\o) (#\t #\h #\i #\s) (#\i #\s) (#\a) (#\t #\e #\s #\t)
;; (#\f #\i #\l #\e))
;; or use above's function:
(map-tree #'string (read-file-to-character-lists "~/test.lisp.txt"))
;; (("h" "e" "l" "l" "o") ("t" "h" "i" "s") ("i" "s") ("a") ("t" "e" "s" "t")
;; ("f" "i" "l" "e"))
;; or:
(defun to-single-letter-strings (s) (cl-ppcre:split "\\s*" s))
(defun read-file-to-letter-lists (file-path)
(mapcan (lambda (s)
(mapcar #'to-single-letter-strings
(string-to-words s)))
(read-file-lines file-path)))
(read-file-to-letter-lists "~/test.lisp.txt")
;; (("h" "e" "l" "l" "o") ("t" "h" "i" "s") ("i" "s") ("a") ("t" "e" "s" "t")
;; ("f" "i" "l" "e"))
I have the list of values and want to take first x values from it and create (list (listof first x values) (listof next x values) and so on until this list gets empty...).
For example, given this list: (list "a" "b" "c" "d" "e" "f" "g" "h" "t")
return this: (list (list a" "b" "c") (list "d" "e" "f") (list "g" "h" "t"))
Thanks in advance :)
Remember what a datatype for a list is. Your class is probably doing something like:
;; A IntegerList is one of:
;; - '()
;; - (cons Integer IntegerList)
Given that, your template should reflect this structure. I will solve the base case (where we want to turn a list of integers into lists of one integers.
First I will define a 1List datatype as:
;; a 1List is:
;; - (cons Integer '())
Next, the purpose statement and signature for the function will be:
;; Takes a list of integers and returns a list of 1Lists of the same integers
;; IntegerList -> 1List
(define (make-1list lst)
...)
Okay cool. Now we need test cases:
(check-expect (make-1list (list 1 2 3)) (list (list 1) (list 2) (list 3)))
(check-expect (make-1list (list)) (list))
(check-expect (make-1list (list 42)) (list (list 42)))
Finally, I can make my template:
(define (make-1list lst)
(cond [(null? lst) ...]
[else ... (first lst) ... (rest lst) ...]))
(Note that it sometimes makes sense to make some of the template first, to help you guide what tests you need.)
Finally, we can fill in our code:
(define (make-1list lst)
(cond [(null? lst) '()]
[else (cons (list (first lst)) (make-1list (rest lst)))]))
And finally, are examples are also tests so we just need to run them to make sure everything works.
Now, since you want to make 3Lists instead of 1Lists, do you see how you can follow this recipe to solve the problem?
Write down your data definition.
Make your purpose statement and signature.
Make your examples.
Make your template.
Write the actual function.
Turn your existing examples into tests.
Following this pattern should help you break the problem down into smaller steps. Good luck.
Better way to accomplish this task is to use accumulators & recursion.
I use 2 methods to build a tree based on cons cells.
(defun make-tree (nodes)
(cons nodes NIL))
(defun add-child (tree child)
(setf (cdr tree) (append (cdr tree) child)))
Then I created 4 parameters:
(defparameter *root* (make-tree "root"))
(defparameter *a* (make-tree "a"))
(defparameter *b* (make-tree "b"))
(defparameter *c* (make-tree "c"))
And I construct the following tree:
(add-child *root* *a*)
(add-child *root* *b*)
(add-child *a* *c*)
The *root* is displayed in the console:
CL-USER> *root*
("root" "a" "b")
My question is: Is it possible to retrieve c from *root*? Something like: (cdr (car (cdr *root*))) returns an error.
You need to use NCONC rather than APPEND in ADD-CHILD, so you don't make copies of the subtrees.
(defun add-child (tree child)
(setf (cdr tree) (append (cdr tree) child)))
With this change, after I do all the other steps, I get:
> *root*
("root" "a" "b" "c")
> (car (cdr (cdr (cdr *root*))))
"c"
> (cadddr *root*)
"c"