940 lines
22 KiB
Plaintext
940 lines
22 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Treating Trees II\n",
|
|
"Let's consider a tree structure, similar to one described [\"Why functional programming matters\" by John Hughes](https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf), that consists of a node value followed by a sequence of zero or more child trees. (The asterisk is meant to indicate the [Kleene star](https://en.wikipedia.org/wiki/Kleene_star).)\n",
|
|
"\n",
|
|
" tree = [] | [node tree*]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## `treestep`\n",
|
|
"In the spirit of `step` we are going to define a combinator `treestep` which expects a tree and three additional items: a base-case function `[B]`, and two quoted programs `[N]` and `[C]`.\n",
|
|
"\n",
|
|
" tree [B] [N] [C] treestep"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"If the current tree node is empty then just execute `B`:\n",
|
|
"\n",
|
|
" [] [B] [N] [C] treestep\n",
|
|
" ---------------------------\n",
|
|
" [] B"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Otherwise, evaluate `N` on the node value, `map` the whole function (abbreviated here as `K`) over the child trees recursively, and then combine the result with `C`.\n",
|
|
"\n",
|
|
" [node tree*] [B] [N] [C] treestep\n",
|
|
" --------------------------------------- w/ K == [B] [N] [C] treestep\n",
|
|
" node N [tree*] [K] map C\n",
|
|
"\n",
|
|
"(Later on we'll experiment with making `map` part of `C` so you can use other combinators.)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Derive the recursive function.\n",
|
|
"We can begin to derive it by finding the `ifte` stage that `genrec` will produce.\n",
|
|
"\n",
|
|
" K == [not] [B] [R0] [R1] genrec\n",
|
|
" == [not] [B] [R0 [K] R1] ifte\n",
|
|
"\n",
|
|
"So we just have to derive `J`:\n",
|
|
"\n",
|
|
" J == R0 [K] R1"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The behavior of `J` is to accept a (non-empty) tree node and arrive at the desired outcome.\n",
|
|
"\n",
|
|
" [node tree*] J\n",
|
|
" ------------------------------\n",
|
|
" node N [tree*] [K] map C"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"So `J` will have some form like:\n",
|
|
"\n",
|
|
" J == ... [N] ... [K] ... [C] ..."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Let's dive in. First, unquote the node and `dip` `N`.\n",
|
|
"\n",
|
|
" [node tree*] uncons [N] dip\n",
|
|
" node [tree*] [N] dip\n",
|
|
" node N [tree*]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next, `map` `K` over the child trees and combine with `C`.\n",
|
|
"\n",
|
|
" node N [tree*] [K] map C\n",
|
|
" node N [tree*] [K] map C\n",
|
|
" node N [K.tree*] C"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"So:\n",
|
|
"\n",
|
|
" J == uncons [N] dip [K] map C"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Plug it in and convert to `genrec`:\n",
|
|
"\n",
|
|
" K == [not] [B] [J ] ifte\n",
|
|
" == [not] [B] [uncons [N] dip [K] map C] ifte\n",
|
|
" == [not] [B] [uncons [N] dip] [map C] genrec"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Extract the givens to parameterize the program.\n",
|
|
"Working backwards:\n",
|
|
"\n",
|
|
" [not] [B] [uncons [N] dip] [map C] genrec\n",
|
|
" [B] [not] swap [uncons [N] dip] [map C] genrec\n",
|
|
" [B] [uncons [N] dip] [[not] swap] dip [map C] genrec\n",
|
|
" ^^^^^^^^^^^^^^^^\n",
|
|
" [B] [[N] dip] [uncons] swoncat [[not] swap] dip [map C] genrec\n",
|
|
" [B] [N] [dip] cons [uncons] swoncat [[not] swap] dip [map C] genrec\n",
|
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Extract a couple of auxiliary definitions:\n",
|
|
"\n",
|
|
" TS.0 == [[not] swap] dip\n",
|
|
" TS.1 == [dip] cons [uncons] swoncat"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
" [B] [N] TS.1 TS.0 [map C] genrec\n",
|
|
" [B] [N] [map C] [TS.1 TS.0] dip genrec\n",
|
|
" [B] [N] [C] [map] swoncat [TS.1 TS.0] dip genrec\n",
|
|
"\n",
|
|
"The givens are all to the left so we have our definition."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### (alternate) Extract the givens to parameterize the program.\n",
|
|
"Working backwards:\n",
|
|
"\n",
|
|
" [not] [B] [uncons [N] dip] [map C] genrec\n",
|
|
" [not] [B] [N] [dip] cons [uncons] swoncat [map C] genrec\n",
|
|
" [B] [N] [not] roll> [dip] cons [uncons] swoncat [map C] genrec\n",
|
|
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define `treestep`"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from notebook_preamble import D, J, V, define, DefinitionWrapper"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"DefinitionWrapper.add_definitions('''\n",
|
|
"\n",
|
|
" _treestep_0 == [[not] swap] dip\n",
|
|
" _treestep_1 == [dip] cons [uncons] swoncat\n",
|
|
" treegrind == [_treestep_1 _treestep_0] dip genrec\n",
|
|
" treestep == [map] swoncat treegrind\n",
|
|
"\n",
|
|
"''', D)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Examples\n",
|
|
"Consider trees, the nodes of which are integers. We can find the sum of all nodes in a tree with this function:\n",
|
|
"\n",
|
|
" sumtree == [pop 0] [] [sum +] treestep"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"define('sumtree == [pop 0] [] [sum +] treestep')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Running this function on an empty tree value gives zero:\n",
|
|
"\n",
|
|
" [] [pop 0] [] [sum +] treestep\n",
|
|
" ------------------------------------\n",
|
|
" 0"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"0\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[] sumtree') # Empty tree."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Running it on a non-empty node:\n",
|
|
"\n",
|
|
" [n tree*] [pop 0] [] [sum +] treestep\n",
|
|
" n [tree*] [[pop 0] [] [sum +] treestep] map sum +\n",
|
|
" n [ ... ] sum +\n",
|
|
" n m +\n",
|
|
" n+m\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"23\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[23] sumtree') # No child trees."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"23\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[23 []] sumtree') # Child tree, empty."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"32\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[23 [2 [4]] [3]] sumtree') # Non-empty child trees."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"49\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[23 [2 [8] [9]] [3] [4 []]] sumtree') # Etc..."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"49\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [] [cons sum] treestep') # Alternate \"spelling\"."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"[23 [23 [23] [23]] [23] [23 []]]\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 23] [cons] treestep') # Replace each node."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"[1 [1 [1] [1]] [1] [1 []]]\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"6\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep sumtree')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"metadata": {
|
|
"scrolled": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"6\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [pop 1] [sum +] treestep') # Combine replace and sum into one function."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"metadata": {
|
|
"scrolled": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"3\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[4 [3 [] [7]]] [pop 0] [pop 1] [sum +] treestep') # Combine replace and sum into one function."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Redefining the Ordered Binary Tree in terms of `treestep`.\n",
|
|
"\n",
|
|
" Tree = [] | [[key value] left right]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"What kind of functions can we write for this with our `treestep`?\n",
|
|
"\n",
|
|
"The pattern for processing a non-empty node is:\n",
|
|
"\n",
|
|
" node N [tree*] [K] map C\n",
|
|
"\n",
|
|
"Plugging in our BTree structure:\n",
|
|
"\n",
|
|
" [key value] N [left right] [K] map C"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Traversal\n",
|
|
" [key value] first [left right] [K] map i\n",
|
|
" key [value] [left right] [K] map i\n",
|
|
" key [left right] [K] map i\n",
|
|
" key [lkey rkey ] i\n",
|
|
" key lkey rkey"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"This doesn't quite work:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"3 'B' 'B'\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[[3 0] [[2 0] [][]] [[9 0] [[5 0] [[4 0] [][]] [[8 0] [[6 0] [] [[7 0] [][]]][]]][]]] [\"B\"] [first] [i] treestep')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Doesn't work because `map` extracts the `first` item of whatever its mapped function produces. We have to return a list, rather than depositing our results directly on the stack.\n",
|
|
"\n",
|
|
"\n",
|
|
" [key value] N [left right] [K] map C\n",
|
|
"\n",
|
|
" [key value] first [left right] [K] map flatten cons\n",
|
|
" key [left right] [K] map flatten cons\n",
|
|
" key [[lk] [rk] ] flatten cons\n",
|
|
" key [ lk rk ] cons\n",
|
|
" [key lk rk ]\n",
|
|
"\n",
|
|
"So:\n",
|
|
"\n",
|
|
" [] [first] [flatten cons] treestep"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"[3 2 9 5 4 8 6 7]\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [first] [flatten cons] treestep')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"There we go."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### In-order traversal\n",
|
|
"\n",
|
|
"From here:\n",
|
|
"\n",
|
|
" key [[lk] [rk]] C\n",
|
|
" key [[lk] [rk]] i\n",
|
|
" key [lk] [rk] roll<\n",
|
|
" [lk] [rk] key swons concat\n",
|
|
" [lk] [key rk] concat\n",
|
|
" [lk key rk]\n",
|
|
"\n",
|
|
"So:\n",
|
|
"\n",
|
|
" [] [i roll< swons concat] [first] treestep"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"[2 3 4 5 6 7 8 9]\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [uncons pop] [i roll< swons concat] treestep')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## With `treegrind`?\n",
|
|
"The `treegrind` function doesn't include the `map` combinator, so the `[C]` function must arrange to use some combinator on the quoted recursive copy `[K]`. With this function, the pattern for processing a non-empty node is:\n",
|
|
"\n",
|
|
" node N [tree*] [K] C\n",
|
|
"\n",
|
|
"Plugging in our BTree structure:\n",
|
|
"\n",
|
|
" [key value] N [left right] [K] C"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"['key' 'value'] 'N' [['left'] ['right']] [[not] ['B'] [uncons ['N'] dip] ['C'] genrec] 'C'\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[[\"key\" \"value\"] [\"left\"] [\"right\"] ] [\"B\"] [\"N\"] [\"C\"] treegrind')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## `treegrind` with `step`"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Iteration through the nodes"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"[3 0] 'N' [2 0] 'N' [9 0] 'N' [5 0] 'N' [4 0] 'N' [8 0] 'N' [6 0] 'N' [7 0] 'N'\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [pop] [\"N\"] [step] treegrind')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Sum the nodes' keys."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 20,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"44\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('0 [[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [pop] [first +] [step] treegrind')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Rebuild the tree using `map` (imitating `treestep`.)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 21,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"[[103 0] [[102 0] [] []] [[109 0] [[105 0] [[104 0] [] []] [[108 0] [[106 0] [] [[107 0] [] []]] []]] []]]\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [[100 +] infra] [map cons] treegrind')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Do we have the flexibility to reimplement `Tree-get`?\n",
|
|
"I think we do:\n",
|
|
"\n",
|
|
" [B] [N] [C] treegrind"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"We'll start by saying that the base-case (the key is not in the tree) is user defined, and the per-node function is just the query key literal:\n",
|
|
"\n",
|
|
" [B] [query_key] [C] treegrind"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"This means we just have to define `C` from:\n",
|
|
"\n",
|
|
" [key value] query_key [left right] [K] C\n",
|
|
" "
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Let's try `cmp`:\n",
|
|
"\n",
|
|
" C == P [T>] [E] [T<] cmp\n",
|
|
"\n",
|
|
" [key value] query_key [left right] [K] P [T>] [E] [T<] cmp"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### The predicate `P`\n",
|
|
"Seems pretty easy (we must preserve the value in case the keys are equal):\n",
|
|
"\n",
|
|
" [key value] query_key [left right] [K] P\n",
|
|
" [key value] query_key [left right] [K] roll<\n",
|
|
" [key value] [left right] [K] query_key [roll< uncons swap] dip\n",
|
|
"\n",
|
|
" [key value] [left right] [K] roll< uncons swap query_key\n",
|
|
" [left right] [K] [key value] uncons swap query_key\n",
|
|
" [left right] [K] key [value] swap query_key\n",
|
|
" [left right] [K] [value] key query_key\n",
|
|
"\n",
|
|
" P == roll< [roll< uncons swap] dip\n",
|
|
"\n",
|
|
"(Possibly with a swap at the end? Or just swap `T<` and `T>`.)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"So now:\n",
|
|
"\n",
|
|
" [left right] [K] [value] key query_key [T>] [E] [T<] cmp\n",
|
|
"\n",
|
|
"Becomes one of these three:\n",
|
|
"\n",
|
|
" [left right] [K] [value] T>\n",
|
|
" [left right] [K] [value] E\n",
|
|
" [left right] [K] [value] T<\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### `E`\n",
|
|
"Easy.\n",
|
|
"\n",
|
|
" E == roll> popop first"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### `T<` and `T>`\n",
|
|
"\n",
|
|
" T< == pop [first] dip i\n",
|
|
" T> == pop [second] dip i"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Putting it together\n",
|
|
"\n",
|
|
"\n",
|
|
" T> == pop [first] dip i\n",
|
|
" T< == pop [second] dip i\n",
|
|
" E == roll> popop first\n",
|
|
" P == roll< [roll< uncons swap] dip\n",
|
|
" \n",
|
|
" Tree-get == [P [T>] [E] [T<] cmp] treegrind\n",
|
|
"\n",
|
|
"To me, that seems simpler than the `genrec` version."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"DefinitionWrapper.add_definitions('''\n",
|
|
"\n",
|
|
" T> == pop [first] dip i\n",
|
|
" T< == pop [second] dip i\n",
|
|
" E == roll> popop first\n",
|
|
" P == roll< [roll< uncons swap] dip\n",
|
|
"\n",
|
|
" Tree-get == [P [T>] [E] [T<] cmp] treegrind\n",
|
|
"\n",
|
|
"''', D)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 23,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from joy.library import FunctionWrapper\n",
|
|
"from joy.utils.stack import pushback\n",
|
|
"\n",
|
|
"\n",
|
|
"@FunctionWrapper\n",
|
|
"def cmp_(stack, expression, dictionary):\n",
|
|
" '''\n",
|
|
" cmp takes two values and three quoted programs on the stack and runs\n",
|
|
" one of the three depending on the results of comparing the two values:\n",
|
|
"\n",
|
|
" a b [G] [E] [L] cmp\n",
|
|
" ------------------------- a > b\n",
|
|
" G\n",
|
|
"\n",
|
|
" a b [G] [E] [L] cmp\n",
|
|
" ------------------------- a = b\n",
|
|
" E\n",
|
|
"\n",
|
|
" a b [G] [E] [L] cmp\n",
|
|
" ------------------------- a < b\n",
|
|
" L\n",
|
|
" '''\n",
|
|
" L, (E, (G, (b, (a, stack)))) = stack\n",
|
|
" expression = pushback(G if a > b else L if a < b else E, expression)\n",
|
|
" return stack, expression, dictionary\n",
|
|
"\n",
|
|
"\n",
|
|
"D['cmp'] = cmp_"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 24,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"15\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('''\\\n",
|
|
"\n",
|
|
"[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]\n",
|
|
"\n",
|
|
"[] [5] Tree-get\n",
|
|
"\n",
|
|
"''')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"'nope'\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"J('''\\\n",
|
|
"\n",
|
|
"[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]\n",
|
|
"\n",
|
|
"[pop \"nope\"] [25] Tree-get\n",
|
|
"\n",
|
|
"''')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 2",
|
|
"language": "python",
|
|
"name": "python2"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 2
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython2",
|
|
"version": "2.7.12"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|