PLEAC examples - 5. Hashes
5.0. Introduction
People and parts of computer programs interact in all sorts of ways.Single scalar variables are like hermits, living a solitary existence whose only meaning comes from within the individual.
Arrays are like cults, where multitudes marshal themselves under the name of a charismatic leader.
In the middle lies the comfortable, intimate ground of the one-to-one relationship that is the hash.
5.0.1. Introduction
# To associate keys with values, PicoLisp uses (besides the built-in database) # 1. Symbol properties ('put', 'get', ':' etc.) # 2. Association lists ('assoc', 'asoq', 'get') # 3. Binary trees ('idx', 'lup') # For the examples in this section we use association lists : (de Age (Nat . 24) (Jules . 25) (Josh . 17) ) -> Age : Age -> ((Nat . 24) (Jules . 25) (Josh . 17)) : (push 'Age (cons 'Nat 24) (cons 'Jules 25) (cons 'Josh 17) ) -> (Josh . 17) : Age -> ((Josh . 17) (Jules . 25) (Nat . 24) (Nat . 24) (Jules . 25) (Josh . 17))References cons de push
5.1. Adding elements to a Hash
You need to add an entry to a hash.5.1..1. Example
: (de FoodColor (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") ) -> FoodColor : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) # Add an element to the hash : (push 'FoodColor '(Raspberry . "pink")) -> (Raspberry . "pink") : FoodColor -> ((Raspberry . "pink") (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) : (prinl "Known foods:") : (for F FoodColor (println (car F))) Known foods: Raspberry Apple Banana Lemon Carrot -> CarrotReferences car de for prinl println push
5.2. Testing for the Presence of a Key in a Hash
You need to know whether a hash has a particular key, regardless of any possible associated value.5.2.1. Example
: (de FoodColor (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") ) -> FoodColor : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) : (for Name '("Banana", "Martini") (prinl Name (if (assoc Name FoodColor) " is a food." " is a drink.")) ) Banana is a food. Martini is a drink. -> " is a drink."References assoc de for if prinl
5.3. Deleting from a Hash
You want to remove an entry from a hash so that it doesn't show up with keys, values, or each. If you were using a hash to associate salaries with employees, and an employee resigned, you'd want to remove their entry from the hash.5.3.1. Example
: (de FoodColor (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") ) -> FoodColor : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) : (de printFoods () (prin "Keys: ") (apply println (mapcar car FoodColor)) (prin "Values: ") (apply println (mapcar cdr FoodColor)) ) -> printFoods # Current hash values : (printFoods) Keys: Raspberry Apple Banana Lemon Carrot Values: "pink" "red" "yellow" "yellow" "orange" -> "orange" # Delete the key 'Banana' : (del (assoc 'Banana FoodColor) 'FoodColor) -> ((Raspberry . "pink") (Apple . "red") (Lemon . "yellow") (Carrot . "orange")) # Hash values after deleting the key 'Banana' : (printFoods) Keys: Raspberry Apple Lemon Carrot Values: "pink" "red" "yellow" "orange" -> "orange"References assoc de del
5.4. Traversing a Hash
You want to perform an action on each entry (i.e., each key-value pair) in a hash.5.4.1. Example 1
: (de FoodColor (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") ) -> FoodColor : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) : (for F FoodColor (prinl (car F) " is " (cdr F)) ) Raspberry is pink Apple is red Lemon is yellow Carrot is orange -> "orange" : (mapc '((Food Color) (prinl Food " is " Color)) (mapcar car FoodColor) (mapcar cdr FoodColor) ) Raspberry is pink Apple is red Lemon is yellow Carrot is orange -> "orange" : (for F (sort FoodColor) (prinl (car F) " is " (cdr F)) ) Apple is red Carrot is orange Lemon is yellow Raspberry is pink -> "pink"References car cdr de for mapc mapcar prinl sort
5.4.2. Example 2
Create an input file called 'travin':abc From: Rory Gallagher def From: Jimi Hendrix ghi From: Eric Clapton jkl From: Rory Gallagher mnoand then run the following PicoLisp code:
(load "@lib/misc.l") (in "travin" (until (eof) (when (match '(~(chop "From: ") @From) (line)) (accu 'From @From 1) ) ) ) (for Person (sort From) (prinl (car Person) ": " (cdr Person)) )giving this output:
Eric Clapton: 1 Jimi Hendrix: 1 Rory Gallagher: 2 -> 2References accu car cdr chop eof for in load match prinl sort until when
5.5. Printing a Hash
You want to print a hash in an idiomatic way.5.5.1. Example
: (de FoodColor (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") ) -> FoodColor : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) : (mapc println FoodColor) (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") -> (Carrot . "orange") : (for X FoodColor (prinl (car X) " => " (cdr X)) ) Apple => red Banana => yellow Lemon => yellow Carrot => orange -> "orange"References car cdr de for mapc prinl println
5.6. Retrieving from a Hash in Insertion Order
The keys and each functions give you the hash elements in a strange order, and you want them in the order in which you inserted them.5.6.1. Example
: (setq FoodColor NIL) -> NIL : FoodColor -> NIL : (queue 'FoodColor (cons 'Banana "Yellow")) -> (Banana . "Yellow") : FoodColor -> ((Banana . "Yellow")) : (queue 'FoodColor (cons 'Apple "Green")) -> (Apple . "Green") : FoodColor -> ((Banana . "Yellow") (Apple . "Green")) : (queue 'FoodColor (cons 'Lemon "Yellow")) -> (Lemon . "Yellow") : FoodColor -> ((Banana . "Yellow") (Apple . "Green") (Lemon . "Yellow")) # In insertion order, the foods are: : (for Food FoodColor (prinl " " (car Food)) ) Banana Apple Lemon -> Lemon # Still in insertion order, the foods' colors are: : (for Food FoodColor (prinl (car Food) " is colored " (cdr Food) ".") ) Banana is colored Yellow. Apple is colored Green. Lemon is colored Yellow. -> "."References car cdr cons for prinl queue setq
5.7. Hashes with Multiple Values Per Key
You want to store more than one value for each key.5.7.1. Example
Create example output similar to the Linux 'who' command called "who-users":abelson tty4 2018-06-12 09:53 (:0.0) coles tty6 2018-06-12 10:01 (:0.0) himanshu tty7 2018-06-12 10:33 (:0.0) abelson tty8 2018-06-12 10:55 (:0.0) himanshu pts/0 2018-06-12 11:47 (:0.0) himanshu pts/1 2018-06-12 12:58 (:0.0)then execute the following PicoLisp code:
# Groups the used terminals per user : (setq Ttys (sort (group (in "who-users" (make (while (read) (link (cons @ (read))) (line) ) ) ) ) ) ) -> ((abelson tty4 tty8) (coles tty6) (himanshu tty7 pts/0 pts/1)) # Same, but different layout : (for U Ttys (prin (car U) ": ") (apply println (cdr U)) ) abelson: tty4 tty8 coles: tty6 himanshu: tty7 pts/0 pts/1 -> pts/1 # Another way : (for U Ttys (prinl (car U) ": " (length (cdr U)) " ttys.") (for Tty (sort (cdr U)) (prinl "^I" Tty " (owned by " (car U) ")") ) ) abelson: 2 ttys. tty4 (owned by abelson) tty8 (owned by abelson) coles: 1 ttys. tty6 (owned by coles) himanshu: 3 ttys. pts/0 (owned by himanshu) pts/1 (owned by himanshu) tty7 (owned by himanshu) -> ")" # Delete all pts from the output (destructively) : (for U Ttys (con U (diff (cdr U) '(pts/0 pts/1))) ) -> (tty7) : Ttys -> ((abelson tty4 tty8) (coles tty6) (himanshu tty7))References apply car cdr char con cons diff for group in length line link make prin prinl println read setq sort while
5.8. Inverting a Hash
Hashes map keys to values.You have a hash and a value for which you want to find the corresponding key.
5.8.1 Example 1
: (setq Surname '((Mickey . Mantle) (Babe . Ruth)) FirstName (mapcar '((X) (cons (cdr X) (car X))) Surname) ) -> ((Mantle . Mickey) (Ruth . Babe)) : (get FirstName 'Mantle) -> MickeyReferences get setq
5.8.2 Example 2
Create the following script as "hashinv":#!/usr/bin/picolisp /usr/lib/picolisp/lib.l (ifn (argv Given) (out 2 (prinl "usage: foodfind food_or_color")) (de Color ("Apple" . "red") ("Banana" . "yellow") ("Lemon" . "yellow") ("Carrot" . "orange") ) (when (assoc Given Color) (prinl Given " is a food with color " (cdr @) ".") ) (when (find '((X) (= Given (cdr X))) Color) (prinl (car @) " is a food with color " Given ".") ) ) (bye)then run the script as follows:
chmod 777 hashinv ./hashinv ./hashinv yellow ./hashinv apple ./hasinv AppleReferences argv assoc bye car cdr de find ifn out prinl when
5.8.3 Example 3
: (de FoodColor (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") ) -> FoodColor : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) : (extract '((F) (and (= "yellow" (cdr F)) (car F))) FoodColor ) -> (Banana Lemon) : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange"))References = and car cdr de extract
5.8.3 Example 4 - the most effective one ...
: (setq Names '( (Mickey . Mantle) (Babe . Ruth) (Alexander . Burger) (John . McCarthy) ) ) -> ((Mickey . Mantle) (Babe . Ruth) (Alexander . Burger) (John . McCarthy)) : (rassoc 'Burger Names) -> (Alexander . Burger)References rassoc setq
5.9. Sorting a Hash
You need to work with the elements of a hash in a particular order.5.9.1. Example
: (de FoodColor (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") ) -> FoodColor : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) # Sort by name of Food : (setq FoodColor (sort FoodColor)) -> ((Apple . "red") (Banana . "yellow") (Carrot . "orange") (Lemon . "yellow")) : FoodColor -> ((Apple . "red") (Banana . "yellow") (Carrot . "orange") (Lemon . "yellow")) # Sort by color of food : (setq FoodColor (by cdr sort FoodColor)) -> ((Carrot . "orange") (Apple . "red") (Banana . "yellow") (Lemon . "yellow")) : FoodColor -> ((Carrot . "orange") (Apple . "red") (Banana . "yellow") (Lemon . "yellow"))References by cdr de setq sort
5.10. Merging Hashes
You need to make a new hash with the entries of two existing hashes.5.10.1. Example
: (de FoodColor (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") ) -> FoodColor : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) # Create a hash called DrinkColor. # Then merge FoodColor + DrinkColor into a new hash called IngestedColor : (setq DrinkColor '((Galliano . "yellow") ("Mai Tai" . "blue")) IngestedColor (append FoodColor DrinkColor) ) -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") (Galliano . "yellow") ("Mai Tai" . "blue")) : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) : DrinkColor -> ((Galliano . "yellow") ("Mai Tai" . "blue")) : IngestedColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") (Galliano . "yellow") ("Mai Tai" . "blue")) # Show unique colors in IngestedColor : (setq AllColors (uniq (mapcar cdr IngestedColor))) -> ("red" "yellow" "orange" "blue")References append cdr de mapcar setq uniq
5.11. Finding Common or Different Keys in Two Hashes
You need to find keys in one hash that are present in another hash or keys in one hash that are not present in another.5.11.1. Example
: (de FoodColor (Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange") ) -> FoodColor : FoodColor -> ((Apple . "red") (Banana . "yellow") (Lemon . "yellow") (Carrot . "orange")) # CitrusColor is a hash mapping citrus food name to its color. : (de CitrusColor (Lemon . "yellow") (Orange . "orange") (Lime . "green") ) -> CitrusColor # Build a list of non-citrus foods : (filter '((F) (not (assoc (car F) CitrusColor))) FoodColor) -> ((Apple . "red") (Banana . "yellow") (Carrot . "orange"))References assoc car de filter not
5.12. Hashing References
When you use keys on a hash whose keys are references, the references that keys returns no longer work.This situation often arises when you want to cross-reference two different hashes.
5.12.1. Example
: (setq Files (extract '((File) (and (info File) (cons File (car @)))) '("/etc/termcap", "/vmunix", "/bin/cat") ) ) -> (("/bin/cat" . 35064)) : (prinl "open files " (glue ", " (mapcar car Files))) open files /bin/cat -> "/bin/cat" : (for F Files (prinl (car F) " is " (cdr F) " bytes long.") ) /bin/cat is 35064 bytes long. -> " bytes long."References and car cdr cons extract for glue info mapcar prinl setq
5.13. Presizing a Hash
You want to preallocate memory for a hash to speed up your program so we won't have to incrementally allocate memory each time a new entry is added to the hash.Often you know the final size of a hash before you start building it up, and it's possible to use this information to speed up your program.
Note: this has no meaning in PicoLisp. All data structures grow dynamically.
5.14. Finding the Most Common Anything
You have an aggregate data structure, such as an array or a hash.You want to know how often each element in the array (or key or value in the hash) occurs.
For instance, if your array contains web server transactions, you might want to find the most commonly requested file. If your hash maps usernames to number of logins, you want to find the most common number of logins.
5.14.1. Example
: (off Count) -> NIL : (for Element '(a b c b c d) (accu 'Count Element 1) ) -> (d . 1) : Count -> ((d . 1) (c . 2) (b . 2) (a . 1))References accu for off
5.15. Representing Relationships Between Data
You want to represent relationships between elements of data - for instance, the mother of relationship in a family tree or parent process for a process table.This is closely related to representing tables in relational databases (tables represent relationships between information) and to representing computer science graph structures (edges represent relationships between nodes).
5.15.1. Example
: (de Father (Cain . Adam) (Abel . Adam) (Seth . Adam) (Enoch . Cain) (Irad . Enoch) (Mehujael . Irad) (Methusael . Mehujael) (Lamech . Methusael) (Jubal . Lamech) (Tubalcain . Lamech) (Enos . Seth) ) -> Father : (de ancestor (Name) (while (assoc Name Father) (setq Name (cdr @)) ) Name ) # Always 'Adam' -> ancestor : (setq Children (mapcar '((L) (cons (cdar L) (mapcar car L))) (by cdr group Father) ) ) -> ((Adam Cain Abel Seth) (Cain Enoch) (Enoch Irad) (Irad Mehujael) (Mehujael Methusael) (Methusael Lamech) (Lamech Jubal Tubalcain) (Seth Enos)) : (de children (Name) (prinl Name " begat " (if (get Children Name) (glue ", " @) "nobody" ) ) ) -> children : (children 'Adam) Adam begat Cain, Abel, Seth -> "Cain, Abel, Seth" : (children 'Enos) Enos begat nobody -> "nobody"References assoc car cdar cdr cons de get glue group if mapcar prinl setq while
5.16. Program: dutree
The dutree program turns the output of du into sorted, indented output.Note: under Windows WSL the Linux 'du' command misbehaves and thus 'dutree' gives incorrect results.
5.16.1. The program dutree
Create the program 'dutree':#!/usr/bin/picolisp /usr/lib/picolisp/lib.l # dutree - print sorted indented rendition of du output (load "@lib/misc.l") # Run du, read input, save directories and sizes (setq *Dirsize # ((name size kids ..) ..) (by length sort (in (list 'du (opt)) (make (while (read) (skip) (link (list (split (line) "/") @)) ) ) ) ) ) # Assign kids (for D *Dirsize (when (assoc (head -1 (car D)) *Dirsize) (conc @ (cons (car D))) ) ) (let Root (car *Dirsize) # Figure out how much is taken up in each directory # that isn't stored in subdirectories. add a new # fake kid called "." containing that much (recur (Root) (let (Size (cadr Root) Cursize Size) (for Kid (cddr Root) (when (assoc Kid *Dirsize) (dec 'Cursize (cadr @)) (recurse @) ) ) (unless (= Size Cursize) (let Dot (append (car Root) '(("."))) (push '*Dirsize (list Dot Cursize)) (conc Root (cons Dot)) ) ) ) ) # Recursively output everything (let (Prefix NIL Width (length (cadr Root))) (recur (Root Prefix Width) (let Name (last (car Root)) (prinl Prefix (align Width (cadr Root)) " " Name) (let? Kids (flip (by cadr sort (mapcar '((K) (assoc K *Dirsize)) (cddr Root)) ) ) (setq Prefix (pack Prefix (align Width "|"))) (setq Width (+ 1 (length Name) (length (cadar Kids)))) (for Kid Kids (recurse Kid Prefix Width) ) ) ) ) ) ) (bye)then run Linux commands:
chmod 777 dutree ./dutree ./dutree /usr ./dutree -a ./dutree -a /binReferences + = align append assoc by bye cadar cadr car cddr conc cons dec flip for head in last length let line link list load make mapcar opt pack prinl push read recur recurse setq skip sort split unless when while
https://picolisp.com/wiki/?pcepleachashes
23jun18 | ArievW |