Genealogy with Rackett

James Carlson
3 min readOct 20, 2018

--

I’ve been learning Racket lately, and having a great time of it — inspired, among other things, by Alexis King’s talks on Hackett A Metaprogrammable Haskell Written in Racket. As an experiment, I decided to see if I could write little genealogy program that extracts an ancestor from a tree of ancestors given a lineage, e.g. ,(f f f) for “father’s father’s father” and (m f m) for “mother’s father’s mother.”

For the code below, you will need to know how to extract heads and tails from lists and combine these to good effect:

> (define a '(1 2 3 4))
> (car a)
1
> (cdr a)
'(2 3 4)
> (car (cdr a))
2
> (cadr a)
2

You get the idea. Thus (caddr a), which is really (car (cdr (cdr a))) is 3. Let’s begin the real work with an imaginary family which we can use for testing:

(define family
'("Thomas: 1949"
("Harold: 1922"
("James: 1897" ("George: 1887") ("Elizabeth 1885"))
("Violet 1900" ("Morris: 1877") ("Maude: 1889"))
)
("Mary: 1925"
("Aaron: 1886" ("Zvi: 1860") ("Rebecca: 1863"))
("Susan 1894" ("Jonathan: 1855") ("Fidelity: 1860"))
)
))

The next step is to define a small set of primitives: one function to return the left child tree of a given node, and one function to return the right child tree:

(define (left-child-tree tree) (cadr tree))
(define (right-child-tree tree) (caddr tree))

Let’s check that it works:

> (left-child-tree family)
'("Harold: 1922"
("James: 1897" ("George: 1887")("Elizabeth 1885"))
("Violet 1900" ("Morris: 1877") ("Maude: 1889")))
> (right-child-tree (left-child-tree family))
'("Violet 1900" ("Morris: 1877") ("Maude: 1889"))
> (right-child-tree (right-child-tree (left-child-tree family)))
'("Maude: 1889")

The correct subtree is returned, and composing primitives works as intended. For later use, let’s introduce some abbreviations:

(define m right-child-tree)  ; mother's tree
(define f left-child-tree) ; father's tree

Recall the goal: to compute ancestors by giving a lineage, e.g., m f m for “mother’s paternal grandmother.”

> (ancestor '(f f) family)     ; paternal grandfather
"James: 1897"
> (ancestor '(m f m) family) ; mother's paternal grandmother
"Rebecca: 1863"

To do this, we need a function apply-nested that takes a quoted list of functions as first argument, then applies them in reverse order to the second argument, as indicated in the example below.

> (define (g x) (* 2 x))
> (define (h x) (+ x 1))
> (define (k x) (* x x))
> (h (g (k 2)))
9
> (apply-nested '(k g h) 2)
9

The apply-nested function is defined using recursion and eval:

(define (apply-nested functions arg)
(if (null? functions)
arg
(apply-nested (cdr functions) ((eval (car functions)) arg)))

Now the ancestor function is easy to write: run apply-nested, then extract the top node of the resulting tree using car:

(define (ancestor lineage tree)
(car
(apply-nested lineage tree)))

All done!

--

--

No responses yet