Pairs are a basic building block in all Lisp languages. It
is a simple two-part value with one called the head and the other
called the tail. You can access the head of a pair with
(pair head) and the tail of a pair with
pt (pair tail).
p := pair 1 2 printf "p is %s\n" p printf "ph of p is %s\n" (ph p) printf "pt of p is %s\n" (pt p)
$ idio simple-pairs p is (1 & 2) ph of p is 1 pt of p is 2
Either of the head or tail can be any other value including other pairs. This leads to some very rich data structures.
It also leads to some tersely named accessors such as
is shorthand for
ph (pt (pt x)).
If the tail of a pair is another pair we can sense a linked list forming where the “data” part is stored in the head.
This can be rather verbose to create manually so there is a
There is also a slightly different printing format which does away
with some of the extra parentheses and “pair separators”,
result is the rather more traditional “list” printed form:
(e1 e2 e3 ...).
l1 := pair 1 (pair 2 (pair 3 #n)) printf "l1 is %s\n" l1 printf "ph of l1 is %s\n" (ph l1) printf "pt of l1 is %s\n" (pt l1) l2 := list 1 2 3 printf "l2 is %s\n" l2 printf "ph of l2 is %s\n" (ph l2) printf "pt of l2 is %s\n" (pt l2)
$ idio simple-lists l1 is (1 2 3) ph of l1 is 1 pt of l1 is (2 3) l2 is (1 2 3) ph of l2 is 1 pt of l2 is (2 3)
#n in the find
l1, above, marks a proper
list. The vast majority of lists are proper lists cleanly ending with
a tail of
#n (nil or null).
list always creates proper
Improper lists appear from time to time where that final tail is not
With a list we can test for membership of that list with variations on the equality functions:
memqtests for membership using
memvtests for membership using
membertests for membership using
These membership functions return
#f if they fail but the
remainder of the list (inclusive of the matching element) if they
p1 := pair 1 2 p2 := pair 1 2 things := list 1 "hello" p1 'symbol printf "things is %s\n" things printf "memq 3 in things is %s\n" (memq 3 things) printf "memq 1 in things is %s\n" (memq 1 things) printf "memq p1 in things is %s\n" (memq p1 things) printf "memv \"hello\" in things is %s\n" (memv "hello" things) printf "member p2 in things is %s\n" (member p2 things)
$ idio list-membership things is (1 "hello" (1 & 2) symbol) memq 3 in things is #f memq 1 in things is (1 "hello" (1 & 2) symbol) memq p1 in things is ((1 & 2) symbol) memv "hello" in things is ("hello" (1 & 2) symbol) member p2 in things is ((1 & 2) symbol)
If we make the “data” part more complicated, in particular, a list itself, we can treat this list of lists as a lookup table where the key is the first element in each list.
These association lists are very common and have equivalent key lookup functions:
assqtests the key using
assvtests the key using
assoctests the key using
;; by quoting the list of lists we prevent the evaluator trying to ;; run functions called a, b and c data := '((a "apple" fruit) (b "banana" fruit) (c "carrot" vegetable)) ;; symbols are *always* eq? sym := 'b printf "assq %s is %s\n" sym (assq sym data)
$ idio alists assq b is (b "banana" fruit)
Last built at 2023-11-09T07:11:44Z+0000 from 77077af (dev) for Idio 0.3