diff --git a/CHANGELOG.md b/CHANGELOG.md
index d678b17..0c8acf6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,22 @@ In the first instance, it lists the changes between consecutive releases of this
However, because this book is a rework of the first edition by Bernard Legrand,
this changelog also marks with [n] content that is new in this rework.
+
+## 0.6.0
+
+ - [n] Add chapter “Tacit Programming”:
+ - [n] add section “Function Composition”:
+ - add section “Operators > Compose”;
+ - [n] add “Atop” `⍤` and “Over” `⍥`;
+ - [n] add section “Binding”:
+ - [n] add example of binding in the right operand of `@`, e.g. in `0@(>∘5)` to replace values larger than 5;
+ - add old “Operators > Commute”:
+ - [n] distinguish between “Commute” and “Selfie”;
+ - [n] add section “Constant” on `A⍨`;
+ - [n] write about trains;
+ - [n] add exercises.
+
+
## 0.5.0
- Add chapter “Operators”:
diff --git a/book/_toc.yml b/book/_toc.yml
index 7df319c..ed41b7d 100644
--- a/book/_toc.yml
+++ b/book/_toc.yml
@@ -14,4 +14,5 @@ sections:
- file: Special-Syntax
- file: Nested-Arrays-Continued
- file: Operators
+- file: Tacit-Programming
- file: Appendices
diff --git a/docs/Appendices.ipynb b/docs/Appendices.ipynb
index 6da64b7..bdc5281 100644
--- a/docs/Appendices.ipynb
+++ b/docs/Appendices.ipynb
@@ -764,6 +764,54 @@
"[The `⎕FMT` System Function](./Execute-and-Format-Control.ipynb#the-fmt-system-function), and\n",
"[Processing Nested Arrays](./Nested-Arrays-Continued.ipynb#processing-nested-arrays)."
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### `weird` - mixed doubly nested matrix"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─────┬────────────┐\n",
+ "│456 │┌──────┬───┐│\n",
+ "│ ││Dyalog│44 ││\n",
+ "│ │├──────┼───┤│\n",
+ "│ ││27 │8 6││\n",
+ "│ ││ │2 4││\n",
+ "│ │└──────┴───┘│\n",
+ "├─────┼────────────┤\n",
+ "│17 51│Twisted │\n",
+ "└─────┴────────────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⎕← weird ← 2 2⍴456 (2 2⍴ 'Dyalog' 44 27 (2 2⍴8 6 2 4)) (17 51) 'Twisted'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Used in\n",
+ "[Mixed and Nested Arrays](./Data-andVariables.ipynb#mixed-and-nested-arrays),\n",
+ "[Pick](./Nested-Arrays-Continued.ipynb#pick),\n",
+ "[Reach Indexing](./Nested-Arrays-Continued.ipynb#reach-indexing), and\n",
+ "[Function Composition](./Tacit-Programming.ipynb#function-composition)."
+ ]
}
],
"metadata": {
diff --git a/docs/Operators.ipynb b/docs/Operators.ipynb
index 548eccf..ad8647a 100644
--- a/docs/Operators.ipynb
+++ b/docs/Operators.ipynb
@@ -5760,7 +5760,7 @@
"id": "round-pickup",
"metadata": {},
"source": [
- "The exact meaning of this output will be explained in [the next chapter](./Tacit-Programming.ipynb#Tree-Diagram).\n",
+ "The exact meaning of this output will be explained in [the next chapter](./Tacit-Programming.ipynb#Inspecting-Tacit-Functions).\n",
"For now, it suffices to understand that it does _not_ the output of `(-@idx) vector`.\n",
"\n",
"In order for APL to know that `idx` is the right _operand_ and `vector` is the right _argument_, we must separate them in some way.\n",
diff --git a/docs/Tacit-Programming.ipynb b/docs/Tacit-Programming.ipynb
new file mode 100644
index 0000000..606f5b1
--- /dev/null
+++ b/docs/Tacit-Programming.ipynb
@@ -0,0 +1,6847 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "reverse-fundamentals",
+ "metadata": {},
+ "source": [
+ "# Tacit Programming\n",
+ "\n",
+ "Tacit programming is a programming paradigm that APL supports.\n",
+ "In order to understand what tacit programming is, we need to know what the word “tacit” means, in English:\n",
+ "\n",
+ " > “Tacit, adjective: understood or implied without being stated.”\n",
+ "\n",
+ "In tacit programming, the thing that is implied without being stated is what arguments the functions receive.\n",
+ "In other words, in tacit programming we create functions by combining other functions _without_ specifying where the arguments go.\n",
+ "This sounds much more confusing than what it really is, so let us study some examples."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "genuine-culture",
+ "metadata": {},
+ "source": [
+ "## Combining Functions with Operators\n",
+ "\n",
+ "### Derived Functions are Tacit\n",
+ "\n",
+ "The simplest example of tacit programming arises from the use of the primitive operators.\n",
+ "Recall these two helper functions from a previous exercise:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "peripheral-stroke",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "Trim ← {3↑⍵}\n",
+ "IsLong ← {3<≢⍵}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "actual-garden",
+ "metadata": {},
+ "source": [
+ "These are two regular dfns.\n",
+ "Now, we can use them to create a function `TrimLong` that will trim all the elements of the argument vector that are too long:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "saved-kazakhstan",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┬───┬─────┬───────┬─────────┐\n",
+ "│1│1 2│1 2 3│1 2 3 4│1 2 3 4 5│\n",
+ "└─┴───┴─────┴───────┴─────────┘\n",
+ "┌─┬───┬─────┬─────┬─────┐\n",
+ "│1│1 2│1 2 3│1 2 3│1 2 3│\n",
+ "└─┴───┴─────┴─────┴─────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "TrimLong ← {(Trim¨)@(IsLong¨) ⍵}\n",
+ "TrimLong ⎕← ⍳¨⍳5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "incorporated-argentina",
+ "metadata": {},
+ "source": [
+ "As it stands, the dfn `TrimLong` has nothing special about.\n",
+ "However, we can get rid of the braces and the omega `⍵` because those things are redundant:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "reliable-metallic",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┬───┬─────┬─────┬─────┐\n",
+ "│1│1 2│1 2 3│1 2 3│1 2 3│\n",
+ "└─┴───┴─────┴─────┴─────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "TrimLong ← (Trim¨)@(IsLong¨)\n",
+ "TrimLong ⍳¨⍳5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "incorporated-madison",
+ "metadata": {},
+ "source": [
+ "This alternative implementation works because the operator _at_ needs two operands to derive a new function, and by providing the two operands, we assign the _derived function_ directly to the variable `TrimLong`.\n",
+ "We do not need to wrap the _derived function_ in a dfn.\n",
+ "In fact, when we learned about _at_ in [the respective section](./Operators.ipynb#At), we learned exactly how the _derived function_ will handle its right argument:\n",
+ "\n",
+ " - the right argument will be passed directly to the right operand of _at_, which is `IsLong¨`; then\n",
+ " - the elements of the right argument for which `IsLong` evaluates to 1 are collected as passed to the left operand `Trim¨`.\n",
+ "\n",
+ "Looking closely at the tacit definition of `TrimLong` we see that we actually have two levels of tacit programming.\n",
+ "Notice how the right operand of the operator _at_ is `IsLong¨`.\n",
+ "Why is it `IsLong¨` and not just `IsLong`?\n",
+ "\n",
+ "The dfn `IsLong` takes a vector and determines if it has more than three elements, but we already know that the right operand of _at_ will receive a nested vector.\n",
+ "In our example above, that was `⍳¨⍳5`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "chubby-silence",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┬───┬─────┬───────┬─────────┐\n",
+ "│1│1 2│1 2 3│1 2 3 4│1 2 3 4 5│\n",
+ "└─┴───┴─────┴───────┴─────────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⍳¨⍳5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "refined-throat",
+ "metadata": {},
+ "source": [
+ "Thus, if we want to determine which nested elements of the argument vector are too long, we need to use the operator _each_ to modify `IsLong`.\n",
+ "The use of _each_ modifies how `IsLong` works and that modification is done tacitly because of the definition of _each_: we do not need to write anything to explain that the function `IsLong` is going to be applied to each scalar of the argument to `IsLong¨`.\n",
+ "\n",
+ "Let us try another tacit definition:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "determined-equivalent",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "MaxWindow ← {⌈/,⍺↓⍵}⌺3 3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "instructional-smooth",
+ "metadata": {},
+ "source": [
+ "The (tacit!) function `MaxWindow` takes a matrix argument and computes the maximum value in every 3 by 3 window:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "stable-referral",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3 1 2 1\n",
+ "2 3 1 2 1 2\n",
+ "3 1 2 1 2 3\n",
+ "1 2 1 2 3 1\n",
+ ""
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⎕← mat ← 4 6⍴(⍳3),⍳2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "marked-array",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3 3 3 3 2 2\n",
+ "3 3 3 3 3 3\n",
+ "3 3 3 3 3 3\n",
+ "3 3 2 3 3 3\n",
+ ""
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "MaxWindow mat"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "activated-housing",
+ "metadata": {},
+ "source": [
+ "Suppose that we want to modify this function so that we can apply it to higher-rank arrays.\n",
+ "Our goal is that the function `MaxWindow` gets applied to each 2-cell (each sub-matrix), so we can do that with the operator _rank_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "floppy-concept",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "HighRankMaxWindow ← MaxWindow⍤2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "altered-science",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3 2 3 4 1\n",
+ "2 3 2 3 4 1 2\n",
+ "3 2 3 4 1 2 3\n",
+ "2 3 4 1 2 3 2\n",
+ "3 4 1 2 3 2 3\n",
+ "\n",
+ "4 1 2 3 2 3 4\n",
+ "1 2 3 2 3 4 1\n",
+ "2 3 2 3 4 1 2\n",
+ "3 2 3 4 1 2 3\n",
+ "2 3 4 1 2 3 2\n",
+ ""
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⎕← cuboid ← 2 5 7⍴(⍳3),(1+⍳3)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "executed-dragon",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3 3 3 4 4 4 4\n",
+ "3 3 4 4 4 4 4\n",
+ "3 4 4 4 4 4 3\n",
+ "4 4 4 4 4 3 3\n",
+ "4 4 4 4 3 3 3\n",
+ "\n",
+ "4 4 3 3 4 4 4\n",
+ "4 4 3 4 4 4 4\n",
+ "3 3 4 4 4 4 4\n",
+ "3 4 4 4 4 4 3\n",
+ "3 4 4 4 4 3 3\n",
+ ""
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "HighRankMaxWindow cuboid"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "sonic-birth",
+ "metadata": {},
+ "source": [
+ "We could have defined `HighRankMaxWindow` directly:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "blond-cycling",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "HighRankMaxWindow ← ({⌈/,⍺↓⍵}⌺3 3)⍤2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "robust-allah",
+ "metadata": {},
+ "source": [
+ "### Operator Binding Order\n",
+ "\n",
+ "We have seen two tacit functions that make use of multiple operators to derive successive functions:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "indonesian-emergency",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "TrimLong ← (Trim¨)@(IsLong¨)\n",
+ "HighRankMaxWindow ← ({⌈/,⍺↓⍵}⌺3 3)⍤2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "entertaining-original",
+ "metadata": {},
+ "source": [
+ "However, both functions have superfluous parenthesis, because we have not been considering the order in which operators bind in Dyalog APL.\n",
+ "\n",
+ "When we have an expression, we do not need to parenthesise from the right.\n",
+ "For example,"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "illegal-messaging",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 3 4 5 6\n",
+ ""
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "1 + (⍳5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "atlantic-graphic",
+ "metadata": {},
+ "source": [
+ "is just"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "passing-master",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 3 4 5 6\n",
+ ""
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "1 + ⍳5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "effective-freight",
+ "metadata": {},
+ "source": [
+ "When using multiple operators together, we do not need to parenthesise from the left.\n",
+ "For example, the function `TrimLong` was defined as"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "olive-sullivan",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "TrimLong ← (Trim¨)@(IsLong¨)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "coupled-sapphire",
+ "metadata": {},
+ "source": [
+ "but it could have been defined as"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "dramatic-failing",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┬───┬─────┬─────┬─────┐\n",
+ "│1│1 2│1 2 3│1 2 3│1 2 3│\n",
+ "└─┴───┴─────┴─────┴─────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "TrimLong ← Trim¨@(IsLong¨)\n",
+ "TrimLong ⍳¨⍳5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "legitimate-sweet",
+ "metadata": {},
+ "source": [
+ "The leftmost set of parentheses was not necessary because operators bind from the left.\n",
+ "Thus, the expression `Trim¨@IsLong¨` would have been equivalent to `((Trim¨)@IsLong)¨`.\n",
+ "This shows that the leftmost set of parentheses is not necessary, whereas the rightmost set **is** necessary, otherwise the rightmost operator _each_ binds to the derived function `Trim¨@IsLong` instead of the dfn `IsLong`.\n",
+ "\n",
+ "Similarly, the definition of `HighRankMaxWindow` has an extra set of parentheses.\n",
+ "Instead of `({⌈/,⍺↓⍵}⌺3 3)⍤2`, we can write"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "north-kenya",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3 3 3 4 4 4 4\n",
+ "3 3 4 4 4 4 4\n",
+ "3 4 4 4 4 4 3\n",
+ "4 4 4 4 4 3 3\n",
+ "4 4 4 4 3 3 3\n",
+ "\n",
+ "4 4 3 3 4 4 4\n",
+ "4 4 3 4 4 4 4\n",
+ "3 3 4 4 4 4 4\n",
+ "3 4 4 4 4 4 3\n",
+ "3 4 4 4 4 3 3\n",
+ ""
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "HighRankMaxWindow ← {⌈/,⍺↓⍵}⌺3 3⍤2\n",
+ "HighRankMaxWindow cuboid"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "flexible-crime",
+ "metadata": {},
+ "source": [
+ "Now, we will learn about a tool that will help us study tacit functions and, in particular, understand what parentheses are needed and which ones are not."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "spare-yeast",
+ "metadata": {},
+ "source": [
+ "## Inspecting Tacit Functions\n",
+ "\n",
+ "The user command `]box` that you first learned in [an early section](./Data-and-Variables.ipynb#Nested-Arrays) can also be used to customise how tacit functions are displayed.\n",
+ "This customisation is done through the switch `-trains`.\n",
+ "\n",
+ "Take a look at the help message below and read the different options for the switch `-trains`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "rotary-large",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "─────────────────────────────────────────────────────────────────────────────── \n",
+ " \n",
+ "]OUTPUT.Box \n",
+ " \n",
+ "Display output with borders indicating shape, type and structure \n",
+ " ]Box [on|off|reset|?] [-style={non|min|mid|max}] [-view={non|min|mid|max}] [-trains={box|tree|parens|def}] [-fns[=off|on]]\n",
+ " \n",
+ "]Box -?? ⍝ for more information and examples \n",
+ " \n",
+ ""
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "]box -?"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "right-norfolk",
+ "metadata": {},
+ "source": [
+ "This switch is called \"trains\" because trains are the more general form of tacit programming in Dyalog.\n",
+ "We will learn about trains in [just a bit](#Function-Trains).\n",
+ "\n",
+ "Let us go through the multiple options available in the subsections that follow."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "graphic-fluid",
+ "metadata": {},
+ "source": [
+ "### Box\n",
+ "\n",
+ "The option `box` draws boxes that indicate the binding order of operators and operands.\n",
+ "Thus, inner boxes indicate that their contents bind first, and the contents of outer boxes bind later, possibly with content from inner boxes.\n",
+ "\n",
+ "A couple of examples will follow."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "infinite-model",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Was -trains=box\n",
+ ""
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "]box -trains=box"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "comprehensive-string",
+ "metadata": {},
+ "source": [
+ "First, we will see how the version of `TrimLong` that does not have superfluous parentheses is represented:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "id": "french-sperm",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─────────┬─┬──────────┐\n",
+ "│┌─────┬─┐│@│┌──────┬─┐│\n",
+ "││{3↑⍵}│¨││ ││{3<≢⍵}│¨││\n",
+ "│└─────┴─┘│ │└──────┴─┘│\n",
+ "└─────────┴─┴──────────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 20,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Trim¨@(IsLong¨)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "starting-intersection",
+ "metadata": {},
+ "source": [
+ "As we can see, with the box diagram, we see that the operands of the operator _at_ are the two boxes on its side:\n",
+ "\n",
+ " - on the left, the box contains two other boxes, the dfn `{3↑⍵}` and the operator `¨`, so we get `{3↑⍵}¨` as the left operand; and\n",
+ " - on the right, the box contains two other boxes, the dfn `{3<≢⍵}` and the operator `¨`, so we get `{3<≢⍵}¨` as the right operand.\n",
+ "\n",
+ "If we drop the right set of parentheses, the box diagram changes to reflect the fact that the rightmost _each_ has as left operand everything else:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "id": "perceived-short",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────────────────────┬─┐\n",
+ "│┌─────────┬─┬──────┐│¨│\n",
+ "││┌─────┬─┐│@│{3<≢⍵}││ │\n",
+ "│││{3↑⍵}│¨││ │ ││ │\n",
+ "││└─────┴─┘│ │ ││ │\n",
+ "│└─────────┴─┴──────┘│ │\n",
+ "└────────────────────┴─┘\n",
+ ""
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Trim¨@IsLong¨"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "genetic-fever",
+ "metadata": {},
+ "source": [
+ "If you look closely, the box diagrams look like nested vectors.\n",
+ "A 2-element vector represents the left operand of an operator and its operator, and a 3-element vector represents the left operand, a dyadic operator, and the right operand.\n",
+ "\n",
+ "Working from the innermost box, the operator _each_ binds with `{3↑⍵}` to create the first derived function `F1`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "ambient-stewart",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─────┬─┐\n",
+ "│{3↑⍵}│¨│\n",
+ "└─────┴─┘\n",
+ ""
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⎕← F1 ← '{3↑⍵}' '¨'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "minimal-boxing",
+ "metadata": {},
+ "source": [
+ "Then, the operator _at_ binds with `F1` on the left and with `{3<≢⍵}` on the right to create the second derived function `F2`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "id": "formed-stretch",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─────────┬─┬──────┐\n",
+ "│┌─────┬─┐│@│{3<≢⍵}│\n",
+ "││{3↑⍵}│¨││ │ │\n",
+ "│└─────┴─┘│ │ │\n",
+ "└─────────┴─┴──────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 23,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⎕← F2 ← F1 '@' '{3<≢⍵}'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "desirable-constraint",
+ "metadata": {},
+ "source": [
+ "Finally, the last _each_ binds with `F2` on the left to create the third and final derived function `F3`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "id": "worst-denver",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────────────────────┬─┐\n",
+ "│┌─────────┬─┬──────┐│¨│\n",
+ "││┌─────┬─┐│@│{3<≢⍵}││ │\n",
+ "│││{3↑⍵}│¨││ │ ││ │\n",
+ "││└─────┴─┘│ │ ││ │\n",
+ "│└─────────┴─┴──────┘│ │\n",
+ "└────────────────────┴─┘\n",
+ ""
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⎕← F3 ← F2 '¨'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "smoking-neutral",
+ "metadata": {},
+ "source": [
+ "Similarly, we can see that `HighRankMaxWindow` does not need any parentheses to be interpreted as we needed:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "id": "rising-mexican",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────────────────┬─┬─┐\n",
+ "│┌────────┬─┬───┐│⍤│2│\n",
+ "││{⌈/,⍺↓⍵}│⌺│3 3││ │ │\n",
+ "│└────────┴─┴───┘│ │ │\n",
+ "└────────────────┴─┴─┘\n",
+ ""
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{⌈/,⍺↓⍵}⌺3 3⍤2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "confident-money",
+ "metadata": {},
+ "source": [
+ "The operator _stencil_ got bound with its operands first, and that derived function was the left operand to _rank_."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "pacific-girlfriend",
+ "metadata": {},
+ "source": [
+ "### Tree\n",
+ "\n",
+ "The option `tree` draws the tacit function in a tree structure, with the top/root of the tree being the operator that binds last.\n",
+ "A monadic operator gets a branch to its left operand and a dyadic operator gets two branches, one for each operand."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "id": "nervous-suggestion",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Was -trains=tree\n",
+ ""
+ ]
+ },
+ "execution_count": 26,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "]box -trains=tree"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "varied-timber",
+ "metadata": {},
+ "source": [
+ "If we inspect the tacit definition of `HighRankMaxWindow` first, it should show the operator _rank_ at the top with a sub-tree on the left to represent the left operand `{⌈/,⍺↓⍵}⌺3 3` and a branch on the right pointing to the right operand `2`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "id": "insured-search",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " ⍤\n",
+ " ┌┴┐\n",
+ " ⌺ 2\n",
+ "┌───┴────┐\n",
+ "{⌈/,⍺↓⍵} (2⍴3)\n",
+ ""
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "HighRankMaxWindow"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "heard-parameter",
+ "metadata": {},
+ "source": [
+ "This tree structure shows that the function `HighRankMaxWindow` is a function derived from the operator _rank_.\n",
+ "Then, to interpret the left operand, we have to inspect the sub-tree on the left:\n",
+ "\n",
+ "```\n",
+ " ⌺\n",
+ "┌───┴────┐\n",
+ "{⌈/,⍺↓⍵} (2⍴3)\n",
+ "```\n",
+ "\n",
+ "The left sub-tree shows that the left operand is a function derived from the operator _stencil_ with a left operand dfn and a right operand vector."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "armed-composition",
+ "metadata": {},
+ "source": [
+ "We can also inspect the tree structure of the expression for `TrimLong` **without** any parentheses:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "id": "continental-error",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " ¨\n",
+ " ┌─┘\n",
+ " @\n",
+ " ┌┴┐\n",
+ " ¨ {3<≢⍵}\n",
+ "┌─┘\n",
+ "{3↑⍵}\n",
+ ""
+ ]
+ },
+ "execution_count": 28,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{3↑⍵}¨@{3<≢⍵}¨"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "comparative-delta",
+ "metadata": {},
+ "source": [
+ "The fact that the root of the tree is the operator _each_ shows that we needed a set of parentheses somewhere.\n",
+ "The tree should have the operator _at_ at the root with another derived function on each branch:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "id": "direct-argument",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " @\n",
+ " ┌──┴──┐\n",
+ " ¨ ¨\n",
+ "┌─┘ ┌─┘\n",
+ "{3↑⍵} {3<≢⍵}\n",
+ ""
+ ]
+ },
+ "execution_count": 29,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{3↑⍵}¨@({3<≢⍵}¨)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "second-thesaurus",
+ "metadata": {},
+ "source": [
+ "### Parens\n",
+ "\n",
+ "The option `-trains=parens` will always add as many parentheses as possible, even if superfluous, to make explicit the binding of the operators and their operators:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "id": "congressional-tablet",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Was -trains=parens\n",
+ ""
+ ]
+ },
+ "execution_count": 30,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "]box -trains=parens"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "id": "identified-miller",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "(({3↑⍵}¨)@{3<≢⍵})¨\n",
+ ""
+ ]
+ },
+ "execution_count": 31,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{3↑⍵}¨@{3<≢⍵}¨"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "id": "square-stopping",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "({⌈/,⍺↓⍵}⌺(2⍴3))⍤2\n",
+ ""
+ ]
+ },
+ "execution_count": 32,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{⌈/,⍺↓⍵}⌺3 3⍤2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "corresponding-technical",
+ "metadata": {},
+ "source": [
+ "Note that if any of the elements to be displayed take up multiple lines, then the function will be displayed as if `]box` were OFF.\n",
+ "This display format may look unusual, so we show two functions in that format so you get acquainted with it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "id": "directed-physiology",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Was ON\n",
+ ""
+ ]
+ },
+ "execution_count": 33,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "]box OFF"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "id": "architectural-bandwidth",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " ∇{3↑⍵}\n",
+ " ∇ ¨ @ ∇{3<≢⍵}\n",
+ " ∇ ¨\n",
+ ""
+ ]
+ },
+ "execution_count": 34,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{3↑⍵}¨@{3<≢⍵}¨"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "id": "major-current",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " ∇{⌈/,⍺↓⍵}\n",
+ " ∇ ⌺ 3 3 ⍤ 2\n",
+ ""
+ ]
+ },
+ "execution_count": 35,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{⌈/,⍺↓⍵}⌺3 3⍤2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "id": "athletic-riverside",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Was OFF\n",
+ ""
+ ]
+ },
+ "execution_count": 36,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "]box ON"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "nutritional-claim",
+ "metadata": {},
+ "source": [
+ "### Def\n",
+ "\n",
+ "The option `-trains=def` will show the simplest expression that still defines the same function."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "id": "underlying-yugoslavia",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Was -trains=def\n",
+ ""
+ ]
+ },
+ "execution_count": 37,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "]box -trains=def"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "suspended-subscriber",
+ "metadata": {},
+ "source": [
+ "For example, if we display our original implementation of `TrimLong` that contained superfluous parenthesis, this option will remove them:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "id": "biological-herald",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "{3↑⍵}¨@({3<≢⍵}¨)\n",
+ ""
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(Trim¨)@(IsLong¨)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "revised-moldova",
+ "metadata": {},
+ "source": [
+ "Whenever you are writing a derived function and are not sure if the operands will bind like you need them to, use these tools to inspect the derived function and understand what you need to do to make sure you define your function correctly.\n",
+ "\n",
+ "Let us reset `]box` to using the option `-trains=tree`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "id": "concerned-concentrate",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "Was -trains=tree\n",
+ ""
+ ]
+ },
+ "execution_count": 39,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "]box -trains=tree"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "precious-tutorial",
+ "metadata": {},
+ "source": [
+ "Deriving functions from operators is the simplest form of tacit programming.\n",
+ "In the sections that follow, we will learn about function composition and trains which provide other mechanisms for tacit programming."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fitting-spare",
+ "metadata": {},
+ "source": [
+ "## Function Composition\n",
+ "\n",
+ "Function composition refers to the act of defining a new function at the expense of smaller functions that get applied in the pattern specified by the combining operators.\n",
+ "\n",
+ "Trains, explained in detail in [this section](#Function-Trains), can also be thought of as a form of function composition, but this section will focus on the three operators that Dyalog provides for function composition.\n",
+ "\n",
+ "The three operators introduced here, _beside_, _atop_, and _over_, are dyadic operators that take their operands and produce a single, composite operation.\n",
+ "One can regard these operators as easy ways of specifying inline \"mini-functions\".\n",
+ "As such, these operators do not add functionality to the language that could not be obtained by other means; they just represent a very convenient notation to express some common patterns.\n",
+ "\n",
+ "We start by introducing _beside_."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "attached-hotel",
+ "metadata": {},
+ "source": [
+ "### Beside\n",
+ "\n",
+ "_Beside_ is a dyadic operator represented by _jot_ `∘`, which you can type with APL+j, as you already know.\n",
+ "In this section, we will cover the forms of _beside_ where the operands are functions.\n",
+ "The section on argument binding ([here](#Binding)) will cover the forms in which _beside_ has an array operand.\n",
+ "\n",
+ "_Beside_ takes the two operand functions and applies them to the argument(s) with a pattern that depends on the valence of the derived function:\n",
+ "\n",
+ " - `F∘G ⍵ ←→ F G ⍵`, both `F` and `G` are used as monadic functions.\n",
+ " - `⍺ F∘G ⍵ ←→ ⍺ F G ⍵`, `F` is used dyadically and `G` is used monadically.\n",
+ "\n",
+ "The patterns shown above can also be represented in diagrams like the ones in the figure below.\n",
+ "\n",
+ "\n",
+ "\n",
+ "The composition with _beside_, `F∘G`, can be interpreted as \"preprocess the right argument of `F` with `G`\".\n",
+ "\n",
+ "The operator _beside_ is rarely used alone.\n",
+ "After all, the expressions above show that instead of writing `F∘G ⍵` one could just write `F G ⍵` or, instead of writing `⍺ F∘G ⍵`, one could just write `⍺ F G ⍵`.\n",
+ "_Beside_ is often used together with the operator _each_, as this may give important advantages for execution time and memory consumption.\n",
+ "Another example usage of _beside_ is to create a derived function to be used together with the operator _reduce_.\n",
+ "We will show examples of these usages below."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "acting-innocent",
+ "metadata": {},
+ "source": [
+ "#### Monadic derived function\n",
+ "\n",
+ "Quite often you would like to apply two monadic functions to each item of an array.\n",
+ "This is very easy to do with the help of the powerful operator _each_.\n",
+ "\n",
+ "Let us look at the simple example in which we just want to find the rank of each item of the variable `weird`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "id": "satisfactory-settlement",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─────┬────────────┐\n",
+ "│456 │┌──────┬───┐│\n",
+ "│ ││Dyalog│44 ││\n",
+ "│ │├──────┼───┤│\n",
+ "│ ││27 │8 6││\n",
+ "│ ││ │2 4││\n",
+ "│ │└──────┴───┘│\n",
+ "├─────┼────────────┤\n",
+ "│17 51│Twisted │\n",
+ "└─────┴────────────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 40,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⎕← weird ← 2 2⍴456 (2 2⍴ 'Dyalog' 44 27 (2 2⍴8 6 2 4)) (17 51) 'Twisted'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "frank-management",
+ "metadata": {},
+ "source": [
+ "The rank of `array` is `≢⍴array`, so the rank of each item of `weird` is:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "id": "requested-strip",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0 2\n",
+ "1 1\n",
+ ""
+ ]
+ },
+ "execution_count": 41,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "≢¨⍴¨weird"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "physical-honey",
+ "metadata": {},
+ "source": [
+ "In the expression above, the `⍴¨` creates a (potentially big) array containing the shape of each item of `weird`.\n",
+ "Then, the `≢¨` gets the length of each vector of the intermediate result.\n",
+ "Remember: the rank of an array is the length of the shape of the array.\n",
+ "\n",
+ "This is inefficient for two reasons:\n",
+ "\n",
+ " 1. Firstly, APL must allocate memory to hold the intermediate array, which will be discarded as soon as the entire expression has been evaluated.\n",
+ "\n",
+ "We can see this intermediate result if we insert a `⎕←` between `≢¨` and `⍴¨`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "id": "indonesian-transport",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┬───┐\n",
+ "│ │2 2│\n",
+ "├─┼───┤\n",
+ "│2│7 │\n",
+ "└─┴───┘\n",
+ "0 2\n",
+ "1 1\n",
+ ""
+ ]
+ },
+ "execution_count": 42,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "≢¨ ⎕← ⍴¨ weird"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "local-battery",
+ "metadata": {},
+ "source": [
+ " 2. Secondly, internally APL must loop through a potentially large number of items twice.\n",
+ "\n",
+ "With the help of _beside_, we can eliminate both problems:\n",
+ "APL only needs to traverse the array once, applying both functions to each item in succession.\n",
+ "During the processing of each item, only a very small intermediate array will be created holding the shape of each item, and it will be discarded before processing the next item:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "id": "smart-happening",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0 2\n",
+ "1 1\n",
+ ""
+ ]
+ },
+ "execution_count": 43,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "≢∘⍴¨ weird"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "median-uncle",
+ "metadata": {},
+ "source": [
+ "The expression `≢∘⍴¨` is another example of tacit programming.\n",
+ "From the example above, we know that `≢∘⍴¨` computes the rank of each item in a nested array, if applied monadically.\n",
+ "However, that expression contains two functions and two operators, and in no way we specify explicitly what arguments go where.\n",
+ "Hence, `≢∘⍴¨` is an example of tacit programming.\n",
+ "\n",
+ "As another example usage of _beside_, consider the expression below:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "id": "narrative-nicaragua",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3 10 28\n",
+ ""
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "+/∘⍳¨ 2 4 7"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "entitled-bearing",
+ "metadata": {},
+ "source": [
+ "This expression adds up the items of `⍳2`, those of `⍳4`, and finally those of `⍳7`.\n",
+ "Using _beside_ to compose the _plus reduction_ and the _index generator_ functions uses up less space than using the operator _each_ twice because the intermediate result would be a nested vector with all the vectors created by the _index generator_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "id": "painted-frame",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌───┬───────┬─────────────┐\n",
+ "│1 2│1 2 3 4│1 2 3 4 5 6 7│\n",
+ "└───┴───────┴─────────────┘\n",
+ "3 10 28\n",
+ ""
+ ]
+ },
+ "execution_count": 45,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "+/¨ ⎕← ⍳¨ 2 4 7"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "celtic-politics",
+ "metadata": {},
+ "source": [
+ "If the initial argument contained more integers and they were all large integers, the intermediate result would be unnecessarily long.\n",
+ "\n",
+ "In the third example that follows, both operands to _beside_ are user-defined functions we have seen before:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "id": "diagnostic-sister",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "Sqrt ← {⍵*0.5}\n",
+ "Average ← {(+/⍵)÷≢⍵}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "id": "boxed-sheriff",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3 3.08221 6 3.53553\n",
+ ""
+ ]
+ },
+ "execution_count": 47,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Sqrt∘Average¨ (11 7)(8 11)(21 51)(16 9)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "guilty-malawi",
+ "metadata": {},
+ "source": [
+ "#### Dyadic derived function\n",
+ "\n",
+ "Here is an example of composition of _times_ and _index generator_ with the operator _beside_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "id": "temporal-drama",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌───┬───────────┬───────────┐\n",
+ "│1 2│10 20 30 40│100 200 300│\n",
+ "└───┴───────────┴───────────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 48,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "1 10 100 ×∘⍳¨ 2 4 3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dental-dominican",
+ "metadata": {},
+ "source": [
+ "The expression above multiplies `1` with `⍳2`, then it multiplies `10` with `⍳4`, and finally, it multiplies `100` with `⍳3`.\n",
+ "\n",
+ "Another example involves the approximation of the **golden mean**, which can be calculated by this infinite series:\n",
+ "\n",
+ "$$\n",
+ "1 +\\div~ 1 +\\div~ 1 +\\div~ 1 +\\div~ 1 +\\div~ 1 +\\div~ \\cdots\n",
+ "$$\n",
+ "\n",
+ "As you can see, we have inserted `+÷` between the items of a series of ones.\n",
+ "This operation is a _reduction_ by `+÷`, but the operator _reduce_ only accepts a single function on its left.\n",
+ "To overcome this, we can use the operator _beside_ to compose the two functions `+` and `÷` together, thereby creating a single, derived function that may be used together with _reduce_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "id": "scheduled-stamp",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1.625\n",
+ ""
+ ]
+ },
+ "execution_count": 49,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "+∘÷/ 1 1 1 1 1 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "id": "perfect-launch",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1.61803\n",
+ ""
+ ]
+ },
+ "execution_count": 50,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "+∘÷/ 50⍴1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "reverse-stevens",
+ "metadata": {},
+ "source": [
+ "### Atop\n",
+ "\n",
+ "_Atop_ is a dyadic operator represented by _jot diaeresis_ `⍤`, which you can type with APL+Shift+J, which is the same glyph as the one used for the operator _rank_.\n",
+ "\n",
+ "The difference between the operator _rank_ and the operator _atop_ lies in the right operand:\n",
+ "\n",
+ " - for the operator _rank_, the right operand is an array; and\n",
+ " - for the operator _atop_, the right operand is a function.\n",
+ "\n",
+ "_Atop_ takes the two operand functions and applies them to the argument(s) with a pattern that depends on the valence of the derived function:\n",
+ "\n",
+ " - `F⍤G ⍵ ←→ F G ⍵`, both `F` and `G` are used as monadic functions and this is exactly the same as `F∘G`.\n",
+ " - `⍺ F⍤G ⍵ ←→ F ⍺ G ⍵`, `F` is used monadically and `G` is used dyadically.\n",
+ "\n",
+ "The patterns shown above can also be represented in diagrams like the ones in the figure below.\n",
+ "\n",
+ "\n",
+ "\n",
+ "The left operand is applied _atop_ the right operand.\n",
+ "In other words, the left operand function is applied after the right operand is applied to the available arguments.\n",
+ "Yet another way of describing the operator _atop_ is by saying that `F⍤G` post-processes the result of `G` with `F`.\n",
+ "\n",
+ "We will show some example usages below.\n",
+ "\n",
+ "Much like _beside_, seen before, and _over_, which will be shown next, the operator _atop_ is typically used in conjunction with other operators, for example _each_ or _reduce_."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "awful-valuable",
+ "metadata": {},
+ "source": [
+ "#### Example usages of atop\n",
+ "\n",
+ "Suppose that you need to determine whether a number comes before or after another number.\n",
+ "This type of comparison can be made with one of the many comparison primitives: `<`, `≤`, `≥`, and `>`.\n",
+ "However, the comparison primitives return Boolean results, which are either 0 or 1.\n",
+ "\n",
+ "If you wanted a more fine-grained comparison that distinguishes whether the left argument is before the right argument, after the right argument, or is the same as the right argument, you could use a derived function with _atop_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "id": "twelve-deviation",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 0 ¯1\n",
+ ""
+ ]
+ },
+ "execution_count": 51,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "5 ×⍤- 2 5 8"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "patent-great",
+ "metadata": {},
+ "source": [
+ "The result of `⍺ ×⍤- ⍵` is one of three values:\n",
+ "\n",
+ " - `1` if `⍺` comes after `⍵`;\n",
+ " - `0` if `⍺` is the same as `⍵`; and\n",
+ " - `¯1` if `⍺` comes before `⍵`.\n",
+ "\n",
+ "The dyadic derived function `×⍤-` can be interpreted as \"the sign of the difference\", which can be seen as post-processing the difference of the two arguments with the function _sign_.\n",
+ "\n",
+ "It is true that the expression shown above could be rewritten without _atop_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "id": "direct-objective",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 0 ¯1\n",
+ ""
+ ]
+ },
+ "execution_count": 52,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "× 5 - 2 5 8"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "technological-congress",
+ "metadata": {},
+ "source": [
+ "But that is not necessarily a better alternative to using the derived function `×⍤-` and, in some cases, rewriting `×⍤-` without the _atop_ may not be an alternative."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "infrared-melissa",
+ "metadata": {},
+ "source": [
+ "#### Function atop tack\n",
+ "\n",
+ "There is a common usage pattern for the operator _atop_ that involves using a tack.\n",
+ "\n",
+ "Consider the monadic derived function `≢⍤⊢⌸` that uses the operator _key_ and the operator _atop_, where the left operand to `⌸` is `≢⍤⊢` because operators bind from the left.\n",
+ "The monadic derived function `≢⍤⊢⌸` counts how many times each unique item appears in its argument:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "id": "worldwide-accountability",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 4 4 2\n",
+ ""
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "≢⍤⊢⌸ 'MISSISSIPPI'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "through-collectible",
+ "metadata": {},
+ "source": [
+ "The result above means that one of the letters show up 1 time, two other letters show up 4 times each, and the fourth letter shows up 2 times.\n",
+ "These counts are in the order of the unique elements, so we can easily find out the letters associated with the counts:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 54,
+ "id": "coastal-november",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "MISP\n",
+ ""
+ ]
+ },
+ "execution_count": 54,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "∪'MISSISSIPPI'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "communist-render",
+ "metadata": {},
+ "source": [
+ "The point of using `F⍤⊢⌸` is that the operator _key_ passes two arguments to its left operand (to give more flexibility to the user) but we only care about one of those, so we use the appropriate tack to select the argument we want, and then apply `F` to that argument.\n",
+ "\n",
+ "A similar pattern is exhibited in this alternative implementation of _n-wise reduction_ as a _dop_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "id": "difficult-warren",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3 5 7 9 11 13 15 17 19\n",
+ ""
+ ]
+ },
+ "execution_count": 55,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "_NWiseReduce ← {⍺⍺/⍤⊢⌺⍺⊢⍵}\n",
+ "2 +_NWiseReduce ⍳10"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "experimental-place",
+ "metadata": {},
+ "source": [
+ "`⍺⍺/⍤⊢⌺⍺` is a single derived function and we can inspect its structure:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "id": "threaded-dating",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " ⌺\n",
+ " ┌┴┐\n",
+ " ⍤ 0\n",
+ " ┌┴┐\n",
+ " / ⊢\n",
+ "┌─┘\n",
+ "{}\n",
+ ""
+ ]
+ },
+ "execution_count": 56,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{}/⍤⊢⌺0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "mature-documentary",
+ "metadata": {},
+ "source": [
+ "In the expression above, we substituted the left operand `⍺⍺` of the dop with `{}` because writing `⍺⍺` outside of a dop gives a `SYNTAX ERROR`.\n",
+ "Similarly, we used `0` as the right operand of `⌺` because we cannot write `⍺` outside of a _dfn_/_dop_.\n",
+ "\n",
+ "So, in inspecting the structure of `⍺⍺/⍤⊢⌺⍺`, we see that `⍺⍺/⍤⊢` is the left operand of the operator _stencil_.\n",
+ "Recall that the left operand of the operator _stencil_ takes two arguments:\n",
+ "\n",
+ " - the left argument gives information about the padding of the current sub-array; and\n",
+ " - the right argument is the sub-array being processed.\n",
+ "\n",
+ "Because we do not need the information about the left argument, we use `⍺⍺/⍤⊢` to apply `⍺⍺/` directly to the right argument."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "moved-colon",
+ "metadata": {},
+ "source": [
+ "### Over\n",
+ "\n",
+ "_Over_ is a dyadic operator represented by _circle diaeresis_ `⍥`, which you can type with APL+Shift+O (that is the letter \"Oh\", and not the number zero).\n",
+ "\n",
+ "_Over_ takes two operand functions and applies them to the argument(s) with a pattern that depends on the valence of the derived function:\n",
+ "\n",
+ " - `F⍥G ⍵ ←→ F G ⍵`, both `F` and `G` are used as monadic functions and this is exactly the same as `F∘G` or `F⍤G`.\n",
+ " - `⍺ F⍥G ⍵ ←→ (G ⍺) F (G ⍵)`, `F` is used dyadically and `G` is used monadically.\n",
+ "\n",
+ "The patterns shown above can also be represented in diagrams like the ones in the figure below.\n",
+ "\n",
+ "\n",
+ "\n",
+ "The usage `F⍥G` of the operator _over_ can be interpreted as \"apply `F` after pre-processing all arguments with `G`\".\n",
+ "\n",
+ "We will show some example usages below.\n",
+ "\n",
+ "Of the three compositional operators discussed so far, _beside_, _atop_, and _over_, the operator _over_ is the one that is more commonly used alone.\n",
+ "\n",
+ "We will show some example usages below.\n",
+ "\n",
+ "How can we check if two words start with the same letter?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "id": "solved-senate",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "word1 ← 'banana'\n",
+ "word2 ← 'bat'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "expressed-venezuela",
+ "metadata": {},
+ "source": [
+ "We can use _first_ to get the first character of each word and see if they match:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "id": "measured-announcement",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 58,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⊃word1) = ⊃word2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "alpine-symbol",
+ "metadata": {},
+ "source": [
+ "Alternatively, we can check for _equality over the first character_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "id": "wanted-pearl",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 59,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "word1 =⍥⊃ word2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "curious-merchandise",
+ "metadata": {},
+ "source": [
+ "Suppose that we represent an interval of numbers with a 2-item vector with the two endpoints.\n",
+ "For example, `0 3.34` would be the interval of all the numbers between `0` and `3.34`.\n",
+ "\n",
+ "The centre of an interval can be computed as such:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "id": "cubic-narrow",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1.67\n",
+ ""
+ ]
+ },
+ "execution_count": 60,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Centre ← {(+/⍵)÷2}\n",
+ "Centre 0 3.34"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "scientific-washington",
+ "metadata": {},
+ "source": [
+ "The distance between two intervals can be defined as the distance between the centres, which we can compute with `-⍥Centre`.\n",
+ "\n",
+ "So, the distance between the intervals `¯1 1` and `0 3.34` is"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "id": "instrumental-approval",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2.17\n",
+ ""
+ ]
+ },
+ "execution_count": 61,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "0 3.34 -⍥Centre ¯1 0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "beginning-psychology",
+ "metadata": {},
+ "source": [
+ "If we swap the order of the two arguments, we see that the distance becomes negative:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "id": "clear-repeat",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "¯2.17\n",
+ ""
+ ]
+ },
+ "execution_count": 62,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "¯1 0 -⍥Centre 0 3.34"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "medical-arrest",
+ "metadata": {},
+ "source": [
+ "This does not make much sense, so we might want to fix this by saying that we want the _absolute value after the difference of the centres_, which is done by using the operator _atop_ to \"post-process\" the result of the subtraction:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "id": "twenty-companion",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2.17\n",
+ ""
+ ]
+ },
+ "execution_count": 63,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "¯1 0 |⍤-⍥Centre 0 3.34"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "id": "constant-purchase",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " ⍥\n",
+ " ┌┴┐\n",
+ " ⍤ {(+/⍵)÷2}\n",
+ "┌┴┐\n",
+ "| -\n",
+ ""
+ ]
+ },
+ "execution_count": 64,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "|⍤-⍥Centre"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ordinary-peninsula",
+ "metadata": {},
+ "source": [
+ "### Comparison of the Three Operators\n",
+ "\n",
+ "The three compositional operators _beside_, _atop_, and _over_, all behave in the same way if the derived function is used monadically.\n",
+ "The difference lies in the dyadic use of the derived function, as the table below shows.\n",
+ "\n",
+ "| Operator | Monadic use | Dyadic use |\n",
+ "| :- | :- | :- |\n",
+ "| `F∘G` | `F∘G ⍵ ←→ F G ⍵` | `⍺ F∘G ⍵ ←→ ⍺ F G ⍵` |\n",
+ "| `F⍤G` | `F⍤G ⍵ ←→ F G ⍵` | `⍺ F⍤G ⍵ ←→ F ⍺ G ⍵` |\n",
+ "| `F⍥G` | `F⍥G ⍵ ←→ F G ⍵` | `⍺ F⍥G ⍵ ←→ (G ⍺) F (G ⍵)` |\n",
+ "\n",
+ "These differences can also be summarised in the diagram seen below:\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "endless-grant",
+ "metadata": {},
+ "source": [
+ "### To Compose or Not To Compose\n",
+ "\n",
+ "As stated in the beginning of this section, the three operators _beside_, _atop_, and _over_, do not add new primitive behaviour.\n",
+ "In fact, for each of those operators, we have shown equivalent expressions that do **not** use the operators and that achieve the same effect.\n",
+ "\n",
+ "However, there are advantages to using compositional operators to create derived functions.\n",
+ "Here are some of those advantages:\n",
+ "\n",
+ " - the derived function can be assigned a name;\n",
+ " - function composition with these operators works inside trains ([which will be introduced soon](#Function-Trains)); and\n",
+ " - these operators clarify the meaning of your programs when used correctly. In other words, good usage of _beside_, _atop_, and _over_, can make it easier to read and understand a program.\n",
+ "\n",
+ "With practice, you will develop a better understanding for when using these operators explicitly is a good choice.\n",
+ "In part, it will also come down to personal taste: some prefer to use plenty of compositional operators and others prefer to use none, but optimal usage of these operators lies somewhere in between those two extremes."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "actual-summer",
+ "metadata": {},
+ "source": [
+ "## Binding\n",
+ "\n",
+ "The glyph _jot_ `∘` has yet another use as a dyadic operator.\n",
+ "If one of the operands, and only one, is an array, then `∘` stands for the operator _bind_.\n",
+ "\n",
+ "The operator _bind_ is used to set a fixed argument (the array operand) to a given function (the function operand).\n",
+ "Depending on whether the array operand is on the left or on the right of the function operand, the operator _bind_ sets the left or right argument of the function, respectively.\n",
+ "\n",
+ "More explicitly, in `array∘F`, the operator _bind_ sets the left argument of the function `F` to be `array`, and in `F∘array`, the operator _bind_ sets the right argument of the function `F` to be `array`.\n",
+ "\n",
+ "Next, we take a look at a few examples of the operator _bind_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "id": "weekly-contemporary",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─────┬───┬────────┬───────┐\n",
+ "│1 2 3│Hou│21 53 78│11 22 0│\n",
+ "└─────┴───┴────────┴───────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 65,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "3∘↑¨ (⍳5) 'Houston' (21 53 78 55) (11 22)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "engaged-settle",
+ "metadata": {},
+ "source": [
+ "This expression applies `3↑` to each of the items of the right argument.\n",
+ "So far, this is not a very good example, as the expression would work and give the same result even without the operator _bind_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "id": "obvious-reservoir",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─────┬───┬────────┬───────┐\n",
+ "│1 2 3│Hou│21 53 78│11 22 0│\n",
+ "└─────┴───┴────────┴───────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 66,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "3 ↑¨ (⍳5) 'Houston' (21 53 78 55) (11 22)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "agricultural-credits",
+ "metadata": {},
+ "source": [
+ "However, binding the value 3 to _take_ makes it possible to combine the function with yet another function, so that we can again obtain the advantage of not creating unnecessary intermediate values:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "id": "concrete-hormone",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─────┬───┬────────┬───────┐\n",
+ "│3 2 1│uoH│78 53 21│0 22 11│\n",
+ "└─────┴───┴────────┴───────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 67,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⌽∘(3∘↑)¨ (⍳5) 'Houston' (21 53 78 55) (11 22)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "stock-edition",
+ "metadata": {},
+ "source": [
+ "Moreover, if the array operand is not a scalar, it may be impossible to omit the operator _bind_.\n",
+ "In the example that follows, the operator _bind_ must be present, otherwise we get a `LENGTH ERROR`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "id": "baking-product",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌───┬──┬─────┬─────┐\n",
+ "│1 2│Ho│21 53│11 22│\n",
+ "│3 4│us│78 55│11 22│\n",
+ "└───┴──┴─────┴─────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 68,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "2 2∘⍴¨ (⍳5) 'Houston' (21 53 78 55) (11 22)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "id": "american-substitute",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "LENGTH ERROR\n",
+ " 2 2⍴¨(⍳5)'Houston'(21 53 78 55)(11 22)\n",
+ " ∧\n"
+ ]
+ }
+ ],
+ "source": [
+ "2 2 ⍴¨ (⍳5) 'Houston' (21 53 78 55) (11 22)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "thirty-salad",
+ "metadata": {},
+ "source": [
+ "When we use the operator _bind_, we create a derived function that is monadic, which means the derived function always takes a single right argument.\n",
+ "Thus, an expression like `(F∘arr1) arr2` evaluates to `arr2 F arr1`, because the operator _bind_ created the derived function `F∘arr1` where the **right** argument of `F` is set to `arr1`.\n",
+ "\n",
+ "For example,"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "id": "worthy-closer",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "4 9 13\n",
+ ""
+ ]
+ },
+ "execution_count": 70,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(*∘0.5) 16 81 169"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "unlikely-video",
+ "metadata": {},
+ "source": [
+ "Once bound to 0.5, the function _power_ behaves like the function square root, which expects a right argument (the number(s) to compute the square root of).\n",
+ "\n",
+ "In this form, the derived function must be parenthesised so that the operand 0.5 is separated from the right argument vector.\n",
+ "Another alternative, that we saw in [the chapter about operators](./Operators.ipynb), is to use a tack:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "id": "every-madagascar",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "4 9 13\n",
+ ""
+ ]
+ },
+ "execution_count": 71,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "*∘0.5 ⊢ 16 81 169"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "weird-wedding",
+ "metadata": {},
+ "source": [
+ "## Commute, Selfie, and Constant\n",
+ "\n",
+ "The three operators _commute_, _selfie_, and _constant_, are the three usages of the glyph _tilde diaeresis_ `⍨`.\n",
+ "By now, you should be able to guess that the key combination to type `⍨` is APL+Shift+T.\n",
+ "After all, the function _without_ `~` is APL+t.\n",
+ "\n",
+ "### Commute and Selfie\n",
+ "\n",
+ "As its name implies, _commute_ is a monadic operator which commutes the arguments of its derived function.\n",
+ "\n",
+ "For example,"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 72,
+ "id": "supreme-australian",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2\n",
+ ""
+ ]
+ },
+ "execution_count": 72,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "4 ÷ 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "appointed-bleeding",
+ "metadata": {},
+ "source": [
+ "but if we use the operator _commute_, then"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "id": "adapted-reply",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0.5\n",
+ ""
+ ]
+ },
+ "execution_count": 73,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "4 ÷⍨ 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "acting-specialist",
+ "metadata": {},
+ "source": [
+ "That is, `x F⍨ y` is equivalent to `y F x`.\n",
+ "\n",
+ "When the derived function `F⍨` is used monadically, as in `F⍨ y`, then the same argument gets used on both sides of the function.\n",
+ "Thus, `F⍨ y` is equivalent to `y F y`.\n",
+ "\n",
+ "For example, `⍴⍨ 3` is equivalent to `3⍴3`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 74,
+ "id": "animated-toolbox",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3 3 3\n",
+ ""
+ ]
+ },
+ "execution_count": 74,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⍴⍨ 3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "decreased-batch",
+ "metadata": {},
+ "source": [
+ "Based only on these simple examples, one might think that the operators _commute_ and _selfie_ are useless (typing `⍴⍨3` is no easier than typing `3⍴3`).\n",
+ "However, both may be used to reduce the number of parentheses needed in an expression.\n",
+ "\n",
+ "For example, suppose we want to create a vector like `3⍴3` or `5⍴5`, using the last item of an arbitrary vector `v`.\n",
+ "\n",
+ "A direct approach would be to write `((≢v)⌷v)⍴(≢v)⌷v` or `(⊃⌽v)⍴⊃⌽v`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "id": "junior-professor",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "v ← 8 3 6 7 4"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 76,
+ "id": "incident-passenger",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "4 4 4 4\n",
+ ""
+ ]
+ },
+ "execution_count": 76,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "((≢v)⌷v)⍴(≢v)⌷v"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 77,
+ "id": "spare-difficulty",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "4 4 4 4\n",
+ ""
+ ]
+ },
+ "execution_count": 77,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⊃⌽v)⍴⊃⌽v"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "atmospheric-garden",
+ "metadata": {},
+ "source": [
+ "The operator _selfie_ allows a simpler expression:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 78,
+ "id": "enhanced-bridges",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "4 4 4 4\n",
+ ""
+ ]
+ },
+ "execution_count": 78,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⍴⍨⊃⌽v"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "finite-detroit",
+ "metadata": {},
+ "source": [
+ "It is not only for \"cosmetic\" reasons that it is desirable to avoid repeating an expression.\n",
+ "It also means that the interpreter only has to evaluate the expression once, possibly saving some execution time.\n",
+ "Furthermore, avoiding a verbatim repetition of a piece of code improves maintainability considerably.\n",
+ "If the expression needs to be modified, it is simply too easy to forget to modify all instances of it, or to make mistakes in some of the modifications.\n",
+ "\n",
+ "Some APL programmers still prefer to use an intermediate variable or an inline direct function to obtain the same benefits in terms of efficiency and maintainability:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 79,
+ "id": "moral-bacon",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "4 4 4 4\n",
+ ""
+ ]
+ },
+ "execution_count": 79,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "last ← ⊃⌽v\n",
+ "last⍴last"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "defined-aerospace",
+ "metadata": {},
+ "source": [
+ "It is mostly a matter of taste which of the possible solutions different programmers prefer.\n",
+ "The case illustrates that the APL language typically allows the same task to be solved in many different ways.\n",
+ "\n",
+ "The operator _commute_ can also allow for simpler expressions.\n",
+ "For example, if we wanted to _compress_ the even numbers of the vector `v`, we would write something like:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 80,
+ "id": "requested-fitness",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "8 6 4\n",
+ ""
+ ]
+ },
+ "execution_count": 80,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(~2|v)/v"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dutch-cause",
+ "metadata": {},
+ "source": [
+ "With the operator _commute_, we can write"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 81,
+ "id": "prescription-black",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "8 6 4\n",
+ ""
+ ]
+ },
+ "execution_count": 81,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "v/⍨~2|v"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "formal-machine",
+ "metadata": {},
+ "source": [
+ "The pattern `array /⍨` can be read as \"the items of `array` that...\".\n",
+ "\n",
+ "The operators _commute_ and _selfie_ can also be helpful in trains.\n",
+ "This will be understandable when we study trains in [the next section](#Function-Trains)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "broke-oregon",
+ "metadata": {},
+ "source": [
+ "### Constant\n",
+ "\n",
+ "The operator _constant_ is a monadic operator which takes an array operand `array`.\n",
+ "The derived function is an ambivalent constant function that always returns `array`.\n",
+ "Thus, `array⍨` is equivalent to the dfn `{array}`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 82,
+ "id": "controlled-communications",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3\n",
+ ""
+ ]
+ },
+ "execution_count": 82,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "1 2 3⍨ v"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 83,
+ "id": "honest-litigation",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3\n",
+ ""
+ ]
+ },
+ "execution_count": 83,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "1 2 3⍨ (⍳5) 'Houston' (21 53 78 55) (11 22)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 84,
+ "id": "pacific-quest",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3\n",
+ ""
+ ]
+ },
+ "execution_count": 84,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "'Cat' (1 2 3⍨) 'Dog'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "piano-flashing",
+ "metadata": {},
+ "source": [
+ "## Function Trains\n",
+ "\n",
+ "A function train, often referred to as just a train, is a function that is derived from a sequence of two or more functions.\n",
+ "This sequence of functions must be isolated from its arguments.\n",
+ "Notice the difference in results between this uninteresting APL expression:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 85,
+ "id": "average-winner",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "8\n",
+ ""
+ ]
+ },
+ "execution_count": 85,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "10 -,+ 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "posted-state",
+ "metadata": {},
+ "source": [
+ "And this one, where the three functions are parenthesised:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 86,
+ "id": "compliant-setup",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "8 12\n",
+ ""
+ ]
+ },
+ "execution_count": 86,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "10 (-,+) 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "permanent-savage",
+ "metadata": {},
+ "source": [
+ "Throughout this section you will learn what `(-,+)` means as a function train and you will understand why the result of `10 (-,+) 2` is the vector `8 12`.\n",
+ "\n",
+ "A function train with two functions is called an atop (or a 2-train) and a function train with three functions is called a fork (or a 3-train).\n",
+ "They are the two building blocks of trains with arbitrary length, and we will start by looking at them."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "understanding-hebrew",
+ "metadata": {},
+ "source": [
+ "### 2-train Atop\n",
+ "\n",
+ "A train with two functions is called an atop, which is also the name of an operator introduced in [an earlier section](#Atop).\n",
+ "The 2-train and the operator _atop_ share their name because they function in the same way:\n",
+ "\n",
+ " - `(F G) ⍵ ←→ F⍤G ⍵ ←→ F G ⍵`; and\n",
+ " - `⍺ (F G) ⍵ ←→ ⍺ F⍤G ⍵ ←→ F ⍺ G ⍵`.\n",
+ "\n",
+ "For example, `(|-)` is _the absolute value of the difference_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 87,
+ "id": "banner-arrangement",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "5\n",
+ ""
+ ]
+ },
+ "execution_count": 87,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "10 (|-) 5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 88,
+ "id": "northern-layer",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "5\n",
+ ""
+ ]
+ },
+ "execution_count": 88,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "5 (|-) 10"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "mental-moment",
+ "metadata": {},
+ "source": [
+ "Much like with functions that are combined with operators, we can inspect trains by typing them in the session:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 89,
+ "id": "classical-anderson",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌┴┐\n",
+ "| -\n",
+ ""
+ ]
+ },
+ "execution_count": 89,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(|-)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "exempt-burning",
+ "metadata": {},
+ "source": [
+ "Trains can also be assigned to names.\n",
+ "When we do so, we do not need to parenthesise the sequence of functions because the functions are already isolated from the arguments:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 90,
+ "id": "searching-prerequisite",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "AbsDiff ← |-"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 91,
+ "id": "rising-floating",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "5\n",
+ ""
+ ]
+ },
+ "execution_count": 91,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "5 AbsDiff 10"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "excited-newcastle",
+ "metadata": {},
+ "source": [
+ "### 3-train Fork\n",
+ "\n",
+ "A train with three functions is called a fork.\n",
+ "In the fork `(F G H)`, the two outer functions `F` and `H` are applied first, and then the function `G` in the middle is applied to the results of the two outer functions.\n",
+ "\n",
+ "If we type a fork in the session, we see a diagram that hints at the fact that origin of the name \"fork\", because the diagram looks like a fork with three tines.\n",
+ "If we use an \"empty\" dfn `{}` as a placeholder for a function, we can see the fork-like diagram:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 92,
+ "id": "impressed-notification",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌──┼──┐\n",
+ "{} {} {}\n",
+ ""
+ ]
+ },
+ "execution_count": 92,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{}{}{}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "brilliant-recipe",
+ "metadata": {},
+ "source": [
+ "#### Monadic Fork\n",
+ "\n",
+ "In the monadic case, we have that `(F G H) ⍵ ←→ (F ⍵) G (H ⍵)`.\n",
+ "For example, the fork `(⌽≡⊢)`, when used monadically, checks if the argument vector is a palindrome.\n",
+ "(Recall that a palindrome is a sequence that reads the same when reversed.)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 93,
+ "id": "confident-trademark",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 93,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⌽≡⊢) 'TACOCAT'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 94,
+ "id": "viral-chancellor",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 94,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⌽≡⊢) 1 2 3 4 3 2 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 95,
+ "id": "piano-elder",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0\n",
+ ""
+ ]
+ },
+ "execution_count": 95,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⌽≡⊢) 'MISSISSIPPI'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "announced-primary",
+ "metadata": {},
+ "source": [
+ "Notice that, in a fork that is used monadically, the two outer functions are used monadically but the middle function is always used dyadically."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "presidential-grocery",
+ "metadata": {},
+ "source": [
+ "#### Dyadic Fork\n",
+ "\n",
+ "In the dyadic case, we have that `⍺ (F G H) ⍵ ←→ (⍺ F ⍵) G (⍺ H ⍵)`.\n",
+ "So, if a fork is used dyadically, both arguments get passed to both outer functions, and then the results are given as arguments to the middle function.\n",
+ "\n",
+ "The train `(-,+)`, from the beginning of this section, was used dyadically, so now we can understand it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 96,
+ "id": "impossible-participation",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "8 12\n",
+ ""
+ ]
+ },
+ "execution_count": 96,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "10 (-,+) 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "daily-humanitarian",
+ "metadata": {},
+ "source": [
+ "Is the same as:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 97,
+ "id": "emotional-passing",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "8 12\n",
+ ""
+ ]
+ },
+ "execution_count": 97,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(10 - 2) , (10 + 2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "buried-correlation",
+ "metadata": {},
+ "source": [
+ "Another good example of a dyadic fork is `(≠⊆⊢)`, which can be used to split a vector on a separator.\n",
+ "Below, you can see this fork being used to split a sentence into words:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 98,
+ "id": "integrated-folks",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────┬──┬─┬────────┬────┬────┬─────┐\n",
+ "│this│is│a│sentence│with│some│words│\n",
+ "└────┴──┴─┴────────┴────┴────┴─────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 98,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "' ' (≠⊆⊢) 'this is a sentence with some words'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "serial-thirty",
+ "metadata": {},
+ "source": [
+ "This fork is equivalent to the following expression:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 99,
+ "id": "august-color",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────┬──┬─┬────────┬────┬────┬─────┐\n",
+ "│this│is│a│sentence│with│some│words│\n",
+ "└────┴──┴─┴────────┴────┴────┴─────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 99,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "sentence ← 'this is a sentence with some words'\n",
+ "(' ' ≠ sentence) ⊆ (' ' ⊢ sentence)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "internal-shadow",
+ "metadata": {},
+ "source": [
+ "Of course, the right tack is used to say that the right argument to `⊆` should be the right argument of the fork, unchanged.\n",
+ "In fact, the expression above can be simplified to"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 100,
+ "id": "round-tucson",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────┬──┬─┬────────┬────┬────┬─────┐\n",
+ "│this│is│a│sentence│with│some│words│\n",
+ "└────┴──┴─┴────────┴────┴────┴─────┘\n",
+ ""
+ ]
+ },
+ "execution_count": 100,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(' ' ≠ sentence) ⊆ sentence"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "strange-nepal",
+ "metadata": {},
+ "source": [
+ "#### Arrays in Forks\n",
+ "\n",
+ "The left tine of a fork does not have to be a function.\n",
+ "In fact, it can be any array, which will then be used as the left argument to the function in the centre of the fork.\n",
+ "\n",
+ "For example, the fork `(1=×)` checks if a number is positive:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 101,
+ "id": "outside-touch",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 101,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(1=×) 13.4"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 102,
+ "id": "manual-senegal",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0\n",
+ ""
+ ]
+ },
+ "execution_count": 102,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(1=×) 0"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 103,
+ "id": "complicated-delay",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0\n",
+ ""
+ ]
+ },
+ "execution_count": 103,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(1=×) ¯73.42"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "received-metadata",
+ "metadata": {},
+ "source": [
+ "However, the right tine of a fork cannot be an array.\n",
+ "The right tine of a fork must be a function.\n",
+ "If it were an array, then APL would not interpret that as a fork, but as a normal APL expression that happens to be parenthesised.\n",
+ "\n",
+ "For example, one might think that `(⊢*0.5)` is a fork that implements the function square root.\n",
+ "However, if you use this \"fork\", this is what happens:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 104,
+ "id": "traditional-telescope",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1.64872 16\n",
+ ""
+ ]
+ },
+ "execution_count": 104,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⊢*0.5) 16"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "continuous-tobago",
+ "metadata": {},
+ "source": [
+ "APL sees the expression `(⊢*0.5) 16` as a 2-item vector, where the first item is `⊢*0.5`, which is just `*0.5`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 105,
+ "id": "mobile-counter",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1.64872\n",
+ ""
+ ]
+ },
+ "execution_count": 105,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "*0.5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "radio-learning",
+ "metadata": {},
+ "source": [
+ "If we wanted to insist on writing the function square root as a train, we could fix this by using the operator _constant_, to turn the value 0.5 into a function that always returns 0.5:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 106,
+ "id": "annual-tiffany",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "4\n",
+ ""
+ ]
+ },
+ "execution_count": 106,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⊢*0.5⍨) 16"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "recreational-trunk",
+ "metadata": {},
+ "source": [
+ "### Longer Trains\n",
+ "\n",
+ "A function train does not have to be limited to two or three functions.\n",
+ "Function trains can have an arbitrarily large size.\n",
+ "\n",
+ "In a function train with four or more functions, APL starts combining functions into 3-trains from the right.\n",
+ "For example, to parse the train `(≢≠⊆⊢)`, APL starts by looking at `≠⊆⊢` as a 3-train, which creates a derived function `T ← ≠⊆⊢`.\n",
+ "Now, we can look at `(≢≠⊆⊢)` as `(≢T)` which is a 2-train, an atop.\n",
+ "`T` is the train that we used to split a sentence into its words, so we see that the train `(≢T)` can be used to count how many words are in a sentence:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 107,
+ "id": "continental-madison",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "5\n",
+ ""
+ ]
+ },
+ "execution_count": 107,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "' ' (≢≠⊆⊢) 'this sentence has five words'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "little-mirror",
+ "metadata": {},
+ "source": [
+ "As a beginner, it is recommended that you use the session to inspect the structure of longer trains:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 108,
+ "id": "considered-jordan",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┴─┐\n",
+ "≢ ┌─┼─┐\n",
+ " ≠ ⊆ ⊢\n",
+ ""
+ ]
+ },
+ "execution_count": 108,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "≢≠⊆⊢"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "separated-cedar",
+ "metadata": {},
+ "source": [
+ "The diagrams that the session draws will help you inspect the structure of the derived function.\n",
+ "\n",
+ "As another example, consider the train `(5<(≢≠⊆⊢))`.\n",
+ "In this train, the parentheses make the structure explicit:\n",
+ "\n",
+ " - the outer train is a fork where the left tine is actually an array (the scalar 5), the middle tine is the function `<` and the right tine is another train; and\n",
+ " - the right tine is the 4-train from before.\n",
+ "\n",
+ "Again, the session helps us visualise this structure:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 109,
+ "id": "australian-majority",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┼───┐\n",
+ "5 < ┌─┴─┐\n",
+ " ≢ ┌─┼─┐\n",
+ " ≠ ⊆ ⊢\n",
+ ""
+ ]
+ },
+ "execution_count": 109,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "5<(≢≠⊆⊢)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "confirmed-thumb",
+ "metadata": {},
+ "source": [
+ "With the train above, we can check, for example, if a sentence has more than five words:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 110,
+ "id": "rolled-israeli",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0\n",
+ ""
+ ]
+ },
+ "execution_count": 110,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "' ' (5<(≢≠⊆⊢)) 'this sentence has five words'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 111,
+ "id": "pressing-management",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 111,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "' ' (5<(≢≠⊆⊢)) 'this longer sentence contains a total of nine words'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "comic-regard",
+ "metadata": {},
+ "source": [
+ "If we look at a 5-train, we see that a 5-train is a fork with a right tine that is also a fork.\n",
+ "In the expression below, we use `{Fi}` as a placeholder for an arbitrary function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 112,
+ "id": "racial-wireless",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────┼─────────┐\n",
+ "{F5} {F4} ┌────┼────┐\n",
+ " {F3} {F2} {F1}\n",
+ ""
+ ]
+ },
+ "execution_count": 112,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{F5} {F4} {F3}{F2}{F1}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "accompanied-cuisine",
+ "metadata": {},
+ "source": [
+ "When APL parses the expression above to create the appropriate derived function, it starts by taking the three rightmost functions and creating a fork, that we will call `T1`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 113,
+ "id": "turned-search",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "T1 ← {F3}{F2}{F1}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "conventional-classic",
+ "metadata": {},
+ "source": [
+ "Then, APL uses that fork `T1` as the right tine of the fork that uses the fourth and fifth functions."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 114,
+ "id": "subject-tobacco",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────┼─────────┐\n",
+ "{F5} {F4} ┌────┼────┐\n",
+ " {F3} {F2} {F1}\n",
+ ""
+ ]
+ },
+ "execution_count": 114,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{F5} {F4} T1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "vulnerable-tiger",
+ "metadata": {},
+ "source": [
+ "Much like in regular expressions, in function trains we can use parenthesis to change the way APL parses things.\n",
+ "For example, if we parenthesise the three central functions, we create a 3-train where the middle tine is itself a fork:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 115,
+ "id": "restricted-vinyl",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─────────┼──────┐\n",
+ "{F5} ┌────┼────┐ {F1}\n",
+ " {F4} {F3} {F2}\n",
+ ""
+ ]
+ },
+ "execution_count": 115,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{F5} ({F4}{F3}{F2}) {F1}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "animal-difficulty",
+ "metadata": {},
+ "source": [
+ "If we type a 6-train, we see that the sixth function is applied atop the corresponding 5-train:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 116,
+ "id": "answering-recognition",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────┴────┐\n",
+ "{F6} ┌────┼─────────┐\n",
+ " {F5} {F4} ┌────┼────┐\n",
+ " {F3} {F2} {F1}\n",
+ ""
+ ]
+ },
+ "execution_count": 116,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{F6} {F5} {F4} {F3}{F2}{F1}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "matched-accident",
+ "metadata": {},
+ "source": [
+ "As you may have guessed by now, the length of the function train determines whether we have an atop or a fork, and that distinction depends on the parity of the length of the train:\n",
+ "\n",
+ " - a function train with an odd number of functions is a fork; and\n",
+ " - a function train with an even number of functions is an atop.\n",
+ "\n",
+ "Naturally, a typical train will get increasingly more difficult to understand (for humans) as it grows, so you should keep that in mind when writing your own trains.\n",
+ "Nonetheless, even long trains have a uniform structure that the tree diagram makes clear:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 174,
+ "id": "respiratory-acoustic",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────┼─────────┐\n",
+ "{F9} {F8} ┌────┼─────────┐\n",
+ " {F7} {F6} ┌────┼─────────┐\n",
+ " {F5} {F4} ┌────┼────┐\n",
+ " {F3} {F2} {F1}\n",
+ ""
+ ]
+ },
+ "execution_count": 174,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{F9}{F8}{F7}{F6}{F5}{F4}{F3}{F2}{F1}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "lyric-cradle",
+ "metadata": {},
+ "source": [
+ "We can exploit the uniformity in the structure of (long) trains to write the specification of how trains of arbitrary length work:\n",
+ "\n",
+ "\n",
+ "***Rule***:\n",
+ "\n",
+ " > - In trains, the functions in odd positions are the functions that receive the arguments of the train directly and those functions are used monadically or dyadically, depending on whether the train is called monadically or dyadically.\n",
+ " > - The functions in even positions are used dyadically with the results of the surrounding functions as arguments.\n",
+ " > - If the leftmost function of a train is in an even position, that function will be applied atop the remainder of the train.\n",
+ "\n",
+ "\n",
+ "Let us inspect an arbitrary train with 8 functions:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 175,
+ "id": "bound-colombia",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────┴────┐\n",
+ "{F8} ┌────┼─────────┐\n",
+ " {F7} {F6} ┌────┼─────────┐\n",
+ " {F5} {F4} ┌────┼────┐\n",
+ " {F3} {F2} {F1}\n",
+ ""
+ ]
+ },
+ "execution_count": 175,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{F8}{F7}{F6}{F5}{F4}{F3}{F2}{F1}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "configured-hostel",
+ "metadata": {},
+ "source": [
+ "The functions in positions 1, 3, 5, and 7, will receive the train arguments directly:\n",
+ "\n",
+ "```\n",
+ "┌────┴────┐\n",
+ "{F8} ┌────┼─────────┐\n",
+ " {F7} {F6} ┌────┼─────────┐\n",
+ " ↑↑↑↑ {F5} {F4} ┌────┼────┐\n",
+ " ↑↑↑↑ {F3} {F2} {F1}\n",
+ " ↑↑↑↑ ↑↑↑↑\n",
+ "```\n",
+ "\n",
+ "Then, the functions in even positions will receive, as arguments, the results of the surrounding functions.\n",
+ "Notice the `←` and `→` next to the intersections of the tree diagram:\n",
+ "\n",
+ "```\n",
+ "┌────┴────┐\n",
+ "{F8} ┌───→┼←────────┐\n",
+ " {F7} {F6} ┌───→┼←────────┐\n",
+ " ↑↑↑↑ {F5} {F4} ┌───→┼←───┐\n",
+ " ↑↑↑↑ {F3} {F2} {F1}\n",
+ " ↑↑↑↑ ↑↑↑↑\n",
+ "```\n",
+ "\n",
+ "Finally, if the leftmost function is in an even position, that function will be applied _atop_ the remainder of the train.\n",
+ "In this example, that means `F8` would be applied to the result returned by `F6`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "sacred-record",
+ "metadata": {},
+ "source": [
+ "### Using Trains\n",
+ "\n",
+ "Tacit programming, and trains in particular, are infamous for being too complicated and convoluted.\n",
+ "This is debatable and there are benefits to using trains.\n",
+ "Some of the associated benefits are concrete and can be measured.\n",
+ "For example, function trains lend themselves nicely to idiom recognition, which means they can be faster:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 117,
+ "id": "saving-destruction",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "154020 445155 812948 17402 834033 557036 691461 355647 475123 447026\n",
+ ""
+ ]
+ },
+ "execution_count": 117,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⎕← 10↑vec ← ?1e6⍴1e6"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 118,
+ "id": "offensive-designation",
+ "metadata": {
+ "tags": [
+ "skip-execution"
+ ]
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " \n",
+ " (vec≥999000)⍳1 → 8.9E¯5 | 0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕ \n",
+ " vec(⍳∘1≥)999000 → 3.7E¯7 | -100% \n",
+ ""
+ ]
+ },
+ "execution_count": 118,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "]runtime -c (vec≥999000)⍳1 vec(⍳∘1≥)999000"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "covered-productivity",
+ "metadata": {},
+ "source": [
+ "Some other advantages of using trains are subjective and/or impossible to quantify.\n",
+ "We will expose some of them here.\n",
+ "\n",
+ "Trains provide a mathematically pure way of specifying data transformations, given that trains are functions derived from the composition of other functions.\n",
+ "Thus, trains are an interesting alternative for when you need to define functions that transform your data without producing side-effects.\n",
+ "This \"purity\", which can be hard to define and somewhat subjective, is what allows function trains to be inverted by the operator _power_, as is shown in [a later subsection](#Inverting-Trains).\n",
+ "\n",
+ "Because function trains do not use any characters to specify the function composition or the way the arguments flow (because trains use tacit programming), it can be argued that trains are a more direct way of expressing pure computations, when compared to dfns or tradfns."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "young-creation",
+ "metadata": {},
+ "source": [
+ "### Carriages in a Train\n",
+ "\n",
+ "Function trains do not need to be composed solely of primitive functions.\n",
+ "Any function can be used in a function train, namely other function trains, dfns, tradfns, and functions derived from operators.\n",
+ "Below, you can find a couple of examples.\n",
+ "\n",
+ "First, we create a fork that checks if a non-empty vector contains only a single unique element.\n",
+ "In this fork, the right tine is a named dfn:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 119,
+ "id": "valid-submission",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "CountUnique ← {≢∪⍵}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 120,
+ "id": "fleet-vocabulary",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0\n",
+ ""
+ ]
+ },
+ "execution_count": 120,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(1=CountUnique) 1 1 1 1 2 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "professional-palestinian",
+ "metadata": {},
+ "source": [
+ "There is nothing preventing us from using the dfn directly:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 121,
+ "id": "welcome-majority",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 121,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(1={≢∪⍵}) 1 1 1 1 1 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "joined-intervention",
+ "metadata": {},
+ "source": [
+ "Now, we replace the dfn with an equivalent formulation using the operator _beside_ to compose the two functions that we need on the right tine:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 122,
+ "id": "confidential-lying",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0\n",
+ ""
+ ]
+ },
+ "execution_count": 122,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(1=≢∘∪) 1 1 1 1 2 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "plastic-garage",
+ "metadata": {},
+ "source": [
+ "Because the derived function is being used monadically, we know that `≢∘∪` is the same as `≢⍤∪` and `≢⍥∪`.\n",
+ "On top of that, we know that `≢⍤∪` is the same as the 2-train `(≢∪)`, so we can replace the right tine with another train:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 123,
+ "id": "painful-swimming",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 123,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(1=(≢∪)) 1 1 1 1 1 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "located-thompson",
+ "metadata": {},
+ "source": [
+ "As you can see, there is a lot of flexibility associated with function trains.\n",
+ "Naturally, making use of this flexibility can cost you in readability.\n",
+ "\n",
+ "For example, compare the two trains that follow:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 124,
+ "id": "another-branch",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 124,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "' ' (≠≢⍤⊆⊢) sentence"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 125,
+ "id": "chinese-mills",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 125,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "' ' (≢≠⊆⊢) sentence"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "specific-mailing",
+ "metadata": {},
+ "source": [
+ "The two trains are equivalent, but they differ in the way we specify that the function _tally_ must be applied.\n",
+ "In the train `(≠≢⍤⊆⊢)`, we use the operator _atop_ to specify we do the _tally of the partition_, whereas in the 4-train `(≢≠⊆⊢)`, we have the _tally_ atop the fork `(≠⊆⊢)`.\n",
+ "\n",
+ "We can even see that the corresponding diagrams differ:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 126,
+ "id": "double-adventure",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┼─┐\n",
+ "≠ ⍤ ⊢\n",
+ " ┌┴┐\n",
+ " ≢ ⊆\n",
+ ""
+ ]
+ },
+ "execution_count": 126,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "≠≢⍤⊆⊢"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 127,
+ "id": "sound-kingston",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┴─┐\n",
+ "≢ ┌─┼─┐\n",
+ " ≠ ⊆ ⊢\n",
+ ""
+ ]
+ },
+ "execution_count": 127,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(≢≠⊆⊢)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fiscal-lawsuit",
+ "metadata": {},
+ "source": [
+ "When possible, avoid using operators inside trains because it breaks the uniform pattern of trains.\n",
+ "\n",
+ "A train with 9 functions is a fork with a uniform structure, as shown by its diagram:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 128,
+ "id": "essential-athens",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌────┼─────────┐\n",
+ "{F9} {F8} ┌────┼─────────┐\n",
+ " {F7} {F6} ┌────┼─────────┐\n",
+ " {F5} {F4} ┌────┼────┐\n",
+ " {F3} {F2} {F1}\n",
+ ""
+ ]
+ },
+ "execution_count": 128,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{F9}{F8}{F7}{F6}{F5}{F4}{F3}{F2}{F1}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "novel-consolidation",
+ "metadata": {},
+ "source": [
+ "If we insert some operators, the diagram loses regularity and becomes harder to follow:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 129,
+ "id": "sustainable-climb",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌──┴───┐\n",
+ "{F9} ┌─┼─────────┐\n",
+ " ∘ {F5} ┌────┼─┐\n",
+ " ┌┴┐ {F4} ⍥ {F1}\n",
+ " ⍤ {F6} ┌─┴──┐\n",
+ " ┌─┴──┐ {F3} {F2}\n",
+ " {F8} {F7}\n",
+ ""
+ ]
+ },
+ "execution_count": 129,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{F9}{F8}⍤{F7}∘{F6}{F5}{F4}{F3}⍥{F2}{F1}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "incorporated-substitute",
+ "metadata": {},
+ "source": [
+ "### Hybrid Function-operators\n",
+ "\n",
+ "Some primitives are both functions and operators, like _reduce_ `/` and _reduce-first_ `⌿`.\n",
+ "One must be careful when using hybrid function-operators in function trains if the intended purpose is to use them as functions.\n",
+ "\n",
+ "Here is an expression that selects the positive numbers of a numeric vector:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 130,
+ "id": "dying-invalid",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "v ← ¯0.4 ¯1.3 ¯0.9 3.2 4.5 0.6 4.9 ¯2 ¯2.3 0.3"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 131,
+ "id": "compound-gibson",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3.2 4.5 0.6 4.9 0.3\n",
+ ""
+ ]
+ },
+ "execution_count": 131,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(0∘1\n",
+ ""
+ ]
+ },
+ "execution_count": 132,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "0 (⊢) v"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "rational-folder",
+ "metadata": {},
+ "source": [
+ "However, it clearly does not work.\n",
+ "The issue is that `/` is being parsed as the operator _reduce_, so it is trying to use `0∘<` as its operand.\n",
+ "To force the glyph _slash_ to be parsed as the function _compress_, one can write the function _compress_ as the right operand of the atop `⊢⍤/`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 133,
+ "id": "miniature-encoding",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3.2 4.5 0.6 4.9 0.3\n",
+ ""
+ ]
+ },
+ "execution_count": 133,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "0 (<⊢⍤/⊢) v"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "united-world",
+ "metadata": {},
+ "source": [
+ "This workaround forces the glyph _slash_ to be seen as the function _compress_ (and not the operator) because the atop `⍤` needs a right operand.\n",
+ "The atop `⊢⍤F` is equivalent to the function `F`, so putting `⊢⍤/` in the train will not change its behaviour.\n",
+ "\n",
+ "Another possible fix is to wrap the glyph in a dfn:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 134,
+ "id": "anonymous-montreal",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3.2 4.5 0.6 4.9 0.3\n",
+ ""
+ ]
+ },
+ "execution_count": 134,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "0 (<{⍺/⍵}⊢) v"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "corresponding-award",
+ "metadata": {},
+ "source": [
+ "### Inverting Trains\n",
+ "\n",
+ "The operator _power_ can take a negative right operand, which will invert the function and then apply it.\n",
+ "An advantage of using compositional operators and/or function trains is that the derived functions are amenable to inversion by the operator _power_.\n",
+ "The operator _power_ can invert some of these functions because they are pure functions, and thus there are formulas that allow the interpreter to know how to invert the functions.\n",
+ "\n",
+ "Below, you can find a train that adds 1 to the square of the argument:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "opening-inspector",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "101\n",
+ ""
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(1+×⍨) 10"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dated-applicant",
+ "metadata": {},
+ "source": [
+ "This train can be inverted by the operator power:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "possible-bobby",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "10\n",
+ ""
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(1+×⍨)⍣¯1 ⊢ 101"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "banner-priest",
+ "metadata": {},
+ "source": [
+ "However, the equivalent dfn cannot be inverted:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "devoted-creation",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "DOMAIN ERROR\n",
+ " {1+×⍨⍵}⍣¯1⊢101\n",
+ " ∧\n"
+ ]
+ }
+ ],
+ "source": [
+ "{1+×⍨⍵}⍣¯1 ⊢ 101"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "existing-february",
+ "metadata": {},
+ "source": [
+ "There are limitations to what (tacit) functions the operator _power_ can invert, but this capability is nonetheless surprising."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "binding-region",
+ "metadata": {},
+ "source": [
+ "## Exercises\n",
+ "\n",
+ "\n",
+ "***Exercise 1***:\n",
+ "\n",
+ "What is the result of the expression `2∘×⍤+/ 3 5`?\n",
+ "What about `2∘×⍤+/ 3 5 7`?\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "worth-collar",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 2***:\n",
+ "\n",
+ "Use the operator _bind_ to create derived functions that accept numeric arrays and return a Boolean mask indicating:\n",
+ "\n",
+ " - what values are positive;\n",
+ " - what values are equal to `¯1`; and\n",
+ " - what values are less than or equal to `3.5`.\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "technical-blair",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 3***:\n",
+ "\n",
+ "Replace the underscores `_` with the correct compositional operators so that the results become correct:\n",
+ "\n",
+ "```APL\n",
+ " 4 5 6 ,_≢ 1 0 1\n",
+ "3 3\n",
+ " 4 5 6 ,_≢ 1 0 1\n",
+ "4 5 6 3\n",
+ " 4 5 6 ,_≢ 1 0 1\n",
+ "1\n",
+ "```\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "silver-consideration",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 4***:\n",
+ "\n",
+ "Replace the underscores `_` with the correct compositional operators so that the results become correct:\n",
+ "\n",
+ "```APL\n",
+ " 2 +/_| 11 6 7 19\n",
+ "3\n",
+ " (⍳6) /⍨_~ 1 0 0 1 0 0\n",
+ "2 3 5 6\n",
+ " 2 ÷_+_÷ 4\n",
+ "1.33333\n",
+ "```\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cross-plenty",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 5***:\n",
+ "\n",
+ "Rewrite the expressions below without parentheses.\n",
+ "Use the operator _commute_ instead.\n",
+ "\n",
+ "```APL\n",
+ " (∪w)⍳w ← 'MISSISSIPPI'\n",
+ "1 2 3 3 2 3 3 2 4 4 2\n",
+ " ≢(' '≠sentence)⊆sentence\n",
+ "7\n",
+ " (2 2,(⌊(≢⎕A)÷4))⍴⎕A\n",
+ "ABCDEF\n",
+ "GHIJKL\n",
+ "\n",
+ "MNOPQR\n",
+ "STUVWX\n",
+ "```\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "mechanical-system",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 6***:\n",
+ "\n",
+ "Take the character vector `sentence` and create a Boolean mask that indicates what elements of `sentence` are blank spaces (`' '`).\n",
+ "That mask, in conjunction with the operators _at_ and _constant_, can also be used to replace all the spaces with a different character.\n",
+ "Replace all spaces with asterisks (`'*'`) using this technique.\n",
+ "\n",
+ "\n",
+ "In typical code, this technique is useful when the Boolean mask is used for other computations other than the replacement with _at_."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "raised-second",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 7***:\n",
+ "\n",
+ "The operator _beside_ in `F∘G` can be interpreted as \"preprocess the right argument of `F` with `G`\".\n",
+ "Implement a dop `_B_` such that `⍺ G _B_ F ⍵ ←→ (G ⍺) F ⍵`.\n",
+ "This operator is sometimes referred to as _behind_ and is interpreted as \"preprocess the left argument of `F` with `G`\".\n",
+ "Try to implement it in terms of _beside_ and _commute_.\n",
+ "\n",
+ "**Hint**: start from `F∘G`.\n",
+ "\n",
+ "\n",
+ "Use these expressions to verify your implementation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 195,
+ "id": "binary-martin",
+ "metadata": {
+ "tags": [
+ "skip-execution"
+ ]
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 195,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "¯2 |_B_+ 5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 196,
+ "id": "intended-scout",
+ "metadata": {
+ "tags": [
+ "skip-execution"
+ ]
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3 4 5 6\n",
+ ""
+ ]
+ },
+ "execution_count": 196,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍳10) (3∘↑_B_,) 4 5 6"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "swiss-chemical",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 8***:\n",
+ "\n",
+ "The dfn `{(+/⍵)÷≢⍵}` computes the average of a numeric vector.\n",
+ "Rewrite it as a fork.\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "abstract-castle",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 9***:\n",
+ "\n",
+ "The monadic tacit function `∘.×⍨∘⍳` computes the multiplication table for the numbers from 1 to the right argument.\n",
+ "Rewrite it as a fork.\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "satellite-tooth",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3 4 5\n",
+ "2 4 6 8 10\n",
+ "3 6 9 12 15\n",
+ "4 8 12 16 20\n",
+ "5 10 15 20 25\n",
+ ""
+ ]
+ },
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "∘.×⍨∘⍳ 5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dedicated-adaptation",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 10***:\n",
+ "\n",
+ "The train `(FG)` behaves the same way as the derived function `F⍤G`.\n",
+ "Write a train that behaves the same way as `F∘G` when `F∘G` is used dyadically.\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "reserved-shift",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 11***:\n",
+ "\n",
+ "The train `(FG)` behaves in the same way as the derived function `F⍤G`.\n",
+ "Write two trains that behave the same way as `F⍥G` when `F⍥G` is used dyadically:\n",
+ "\n",
+ " - one train that uses nested trains but no compositional operators; and\n",
+ " - one train that uses compositional operators but no nested trains.\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "substantial-system",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 12***:\n",
+ "\n",
+ "Write a train that behaves like `G _B_ F` when `G _B_ F` is used dyadically, assuming `_B_` is the operator _behind_ from a previous exercise.\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "subjective-voice",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 13***:\n",
+ "\n",
+ "Rewrite the dfns that follow as function trains:\n",
+ "\n",
+ "```APL\n",
+ " {|×⍵} ¯3.14\n",
+ "1\n",
+ " {*2×⍵} 2\n",
+ "54.5982\n",
+ " {2+5×⍵} ¯3.14\n",
+ "¯13.7\n",
+ " {⍵-2*⍵} 0.5\n",
+ "¯0.914214\n",
+ " {|⍵-2*⍵} 0.5\n",
+ "0.914214\n",
+ " {(⍵*2)+⍵*3} 2\n",
+ "12\n",
+ "```\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "eleven-purse",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 14***:\n",
+ "\n",
+ "Rewrite the dfns that follow as function trains:\n",
+ "\n",
+ "```APL\n",
+ " 3 {÷⍵-⍺} 4\n",
+ "1\n",
+ " 3 {(⍺+⍵)×⍺-⍵} 4\n",
+ "¯7\n",
+ " 3 {1+(⍺+⍵)×⍺-⍵} 4\n",
+ "¯6\n",
+ " 3 {⍵*1+⍺} 4\n",
+ "256\n",
+ " 3 {(⍳⍵)*1+⍺} 4\n",
+ "1 16 81 256\n",
+ " 3 {⍵*1+⍳⍺} 4\n",
+ "16 64 256\n",
+ "```\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "convinced-watson",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Exercise 15***:\n",
+ "\n",
+ "The dfn `{,1↑⍵+⍉⍵}` is a monadic dfn that accepts numeric matrix that are square (that is, that have the same number of rows and columns).\n",
+ "Of the trains below, which ones are equivalent to this dfn when called monadically?\n",
+ "\n",
+ " 1. `(,1↑⊢+⍉)`\n",
+ " 2. `(,∘1∘↑⊢+⍉)`\n",
+ " 3. `(,⍉1∘↑⍤+⊢)`\n",
+ " 4. `(,1↑+∘⍉⍨)`\n",
+ " 5. `((,1∘↑)⍉+⊢)`\n",
+ " 6. `(,∘(1∘↑)⊢+⍉)`\n",
+ " 7. `(≢↑⍉,⍤+⊢)`\n",
+ "\n",
+ "You are welcome to inspect the (tree) diagrams of the trains, but do not run the trains.\n",
+ "The objective of the exercise is to analyse the trains and reason about them.\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "grand-graduate",
+ "metadata": {},
+ "source": [
+ "## Solutions\n",
+ "\n",
+ "\n",
+ "***Solution 1***:\n",
+ "\n",
+ "What is the result of the expression `2∘×⍤+/ 3 5`?\n",
+ "What about `2∘×⍤+/ 3 5 7`?\n",
+ "\n",
+ "Because operators bind from the left, `2∘×⍤+/` is equivalent to `((2∘×)⍤+)/`.\n",
+ "The leftmost function is `2∘×` which is _times_ bound to a left argument 2, which is the function _double_.\n",
+ "The function _double_ is being used _atop_ the function _plus_, so the reduction is a _reduction by addition followed by doubling_.\n",
+ "Therefore, the expression `2∘×⍤+/ 3 5` will result in 16:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 135,
+ "id": "norman-replication",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "16\n",
+ ""
+ ]
+ },
+ "execution_count": 135,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "2∘×⍤+/ 3 5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 136,
+ "id": "prompt-executive",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "16\n",
+ ""
+ ]
+ },
+ "execution_count": 136,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "2∘×3+5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fitted-dispute",
+ "metadata": {},
+ "source": [
+ "Similarly, if the vector argument is `3 5 7`, we start by adding 5 and 7 and doubling, which gives 24.\n",
+ "Then, we add 3 to 24 and double, which gives 54:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 137,
+ "id": "intermediate-subsection",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "54\n",
+ ""
+ ]
+ },
+ "execution_count": 137,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "2∘×⍤+/ 3 5 7"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "endless-poster",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 2***:\n",
+ "\n",
+ "Positive values are greater than 0, which can be computed with this derived function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 138,
+ "id": "twenty-disposal",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " ∘\n",
+ "┌┴┐\n",
+ "> 0\n",
+ ""
+ ]
+ },
+ "execution_count": 138,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ ">∘0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "common-romania",
+ "metadata": {},
+ "source": [
+ "Values equal to `¯1` can be computed with:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 139,
+ "id": "victorian-belfast",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " ∘\n",
+ "┌┴─┐\n",
+ "¯1 =\n",
+ ""
+ ]
+ },
+ "execution_count": 139,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "¯1∘="
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cross-victim",
+ "metadata": {},
+ "source": [
+ "Values less than or equal to 3.5 can be found with:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 140,
+ "id": "welsh-christmas",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " ∘\n",
+ "┌┴┐\n",
+ "≤ 3.5\n",
+ ""
+ ]
+ },
+ "execution_count": 140,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "≤∘3.5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "automated-rescue",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 3***:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 141,
+ "id": "olive-working",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3 3\n",
+ ""
+ ]
+ },
+ "execution_count": 141,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "4 5 6 ,⍥≢ 1 0 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 142,
+ "id": "quick-gamma",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "4 5 6 3\n",
+ ""
+ ]
+ },
+ "execution_count": 142,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "4 5 6 ,∘≢ 1 0 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 143,
+ "id": "material-chester",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 143,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "4 5 6 ,⍤≢ 1 0 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "stuck-wells",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 4***:\n",
+ "\n",
+ "Replace the underscores `_` with the correct compositional operators so that the results become correct:\n",
+ "\n",
+ "```APL\n",
+ " 2 +/_| 11 6 7 19\n",
+ "3\n",
+ " (⍳6) /⍨_~ 1 0 0 1 0 0\n",
+ "2 3 5 6\n",
+ " 2 ÷_+_÷ 4\n",
+ "1.33333\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 144,
+ "id": "owned-wealth",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "3\n",
+ ""
+ ]
+ },
+ "execution_count": 144,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "2 +/⍤| 11 6 7 19"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 145,
+ "id": "rotary-final",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 3 5 6\n",
+ ""
+ ]
+ },
+ "execution_count": 145,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍳6) /⍨∘~ 1 0 0 1 0 0"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 146,
+ "id": "meaningful-visit",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1.33333\n",
+ ""
+ ]
+ },
+ "execution_count": 146,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "2 ÷⍤+⍥÷ 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "numeric-friday",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 5***:\n",
+ "\n",
+ "Rewrite the expressions below without parentheses.\n",
+ "Use the operator _commute_ instead.\n",
+ "\n",
+ "```APL\n",
+ " (∪w)⍳w ← 'MISSISSIPPI'\n",
+ "1 2 3 3 2 3 3 2 4 4 2\n",
+ " ≢(' '≠sentence)⊆sentence\n",
+ "7\n",
+ " ((⌊(≢⎕A)÷9),3 3)⍴⎕A\n",
+ "ABC\n",
+ "DEF\n",
+ "GHI\n",
+ "\n",
+ "JKL\n",
+ "MNO\n",
+ "PQR\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 162,
+ "id": "going-blair",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3 3 2 3 3 2 4 4 2\n",
+ ""
+ ]
+ },
+ "execution_count": 162,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "w⍳⍨∪w ← 'MISSISSIPPI'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 163,
+ "id": "subject-photography",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 163,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "≢sentence⊆⍨' '≠sentence"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 168,
+ "id": "valuable-accordance",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "ABC\n",
+ "DEF\n",
+ "GHI\n",
+ "\n",
+ "JKL\n",
+ "MNO\n",
+ "PQR\n",
+ ""
+ ]
+ },
+ "execution_count": 168,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "⎕A⍴⍨3 3,⍨⌊9÷⍨≢⎕A"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "welcome-rebel",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 6***:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 169,
+ "id": "steady-flood",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "mask ← ' '=sentence"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 170,
+ "id": "cloudy-ability",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "6\n",
+ ""
+ ]
+ },
+ "execution_count": 170,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "+/mask ⍝ number of blank spaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "juvenile-coordinate",
+ "metadata": {},
+ "source": [
+ "The operator _constant_ `⍨` turns the vector `mask` into a function that returns the mask that determines where the asterisk is going to be put:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 171,
+ "id": "quarterly-layout",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "this*is*a*sentence*with*some*words\n",
+ ""
+ ]
+ },
+ "execution_count": 171,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "'*'@(mask⍨)sentence"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "british-danish",
+ "metadata": {},
+ "source": [
+ "If the mask had not been calculated previously, we could achieve the same effect with a right operand dfn that _computes_ the mask:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 172,
+ "id": "european-analysis",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "this*is*a*sentence*with*some*words\n",
+ ""
+ ]
+ },
+ "execution_count": 172,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "'*'@{' '=⍵}sentence"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "hollywood-glory",
+ "metadata": {},
+ "source": [
+ "We could even use a compositional operator:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 173,
+ "id": "controlling-bermuda",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "this*is*a*sentence*with*some*words\n",
+ ""
+ ]
+ },
+ "execution_count": 173,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "'*'@(' '∘=)sentence"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "competitive-mission",
+ "metadata": {},
+ "source": [
+ "In this exercise, we tried to emulate the context in which the mask has already been computed because it was used for something else.\n",
+ "In cases like this, it is not needed to recompute the mask."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "revolutionary-transcription",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 7***:\n",
+ "\n",
+ "A direct implementation could be:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 197,
+ "id": "compound-tuner",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "_B_ ← {(⍺⍺ ⍺)⍵⍵ ⍵}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 198,
+ "id": "persistent-melbourne",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 198,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "¯2 |_B_+ 5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 199,
+ "id": "color-brunswick",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3 4 5 6\n",
+ ""
+ ]
+ },
+ "execution_count": 199,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍳10) (3∘↑_B_,) 4 5 6"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "spanish-actress",
+ "metadata": {},
+ "source": [
+ "Alternatively, one can implement _behind_ in terms of _beside_ and _commute_.\n",
+ "Using the hint, we can start with `F∘G`.\n",
+ "\n",
+ "We want to have `⍺ G _B_ F ⍵ ←→ (G ⍺) F ⍵` and we have `⍺ G _B_ F ⍵ ←→ ⍺ F∘G ⍵ ←→ ⍺ F G ⍵`.\n",
+ "The first thing that is wrong with this version is that `G` is being applied to `⍵` and not `⍺`, so we can fix this by using the operator _commute_ once.\n",
+ "\n",
+ "If we have `F∘G⍨`, then `⍺ F∘G⍨ ⍵ ←→ ⍵ F∘G ⍺ ←→ ⍵ F G ⍺`.\n",
+ "Thus, `G` is being applied to the correct argument, but then `F` is getting its arguments `⍵` and `G ⍺` in the wrong order.\n",
+ "To fix this, we need to apply a second _commute_ to the function `F` alone.\n",
+ "\n",
+ "If we use the pattern `F⍨∘G⍨` then we have `⍺ F⍨∘G~ ⍵ ←→ ⍵ F⍨∘G ⍺ ←→ ⍵ F⍨ G ⍺ ←→ (G ⍺) F ⍵`, which is what we wanted.\n",
+ "We can implement this in our dop:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 200,
+ "id": "discrete-slope",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "_B_ ← {⍺ ⍵⍵⍨∘⍺⍺⍨ ⍵}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 201,
+ "id": "forward-silly",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 201,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "¯2 |_B_+ 5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 202,
+ "id": "vocal-courage",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3 4 5 6\n",
+ ""
+ ]
+ },
+ "execution_count": 202,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍳10) (3∘↑_B_,) 4 5 6"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "monetary-applicant",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 8***:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 203,
+ "id": "curious-trading",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "Avg ← +/÷≢"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 204,
+ "id": "varying-review",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2.5\n",
+ ""
+ ]
+ },
+ "execution_count": 204,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Avg 1 2 3 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "supported-belly",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 9***:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "vertical-poverty",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 2 3 4 5\n",
+ "2 4 6 8 10\n",
+ "3 6 9 12 15\n",
+ "4 8 12 16 20\n",
+ "5 10 15 20 25\n",
+ ""
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍳∘.×⍳) 5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "julian-setting",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 10***:\n",
+ "\n",
+ "When `F∘G` is used dyadically, we have `⍺ F∘G ⍵ ←→ ⍺ F G ⍵ ←→ (⍺⊣⍵) F G ⍵ ←→ (⍺⊣⍵) F (G ⍺⊢⍵) ←→ (⍺⊣⍵) F (⍺ G⍤⊢ ⍵)`.\n",
+ "Therefore, the train `(⊣FG⍤⊢)` is the same as `F∘G` when both are used dyadically."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 205,
+ "id": "satisfied-architecture",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 205,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "5 +∘| ¯2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 206,
+ "id": "armed-crown",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 206,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "5 (⊣+|⍤⊢) ¯2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "female-skiing",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 11***:\n",
+ "\n",
+ "When `F⍥G` is used dyadically, we have `⍺ F⍥G ⍵ ←→ (G ⍺) F (G ⍵) ←→ (G ⍺⊣⍵) F (G ⍺⊢⍵)`.\n",
+ "Both `G ⍺⊣⍵` and `G ⍺⊢⍵` exhibit the pattern of an atop, which can be written as a 2-train or with the operator _atop_.\n",
+ "\n",
+ "If we use 2-trains, we get `((G⊣)F(G⊢))`.\n",
+ "If we use the operator _atop_, we get `(G⍤⊣FG⍤⊢)`.\n",
+ "These are the same as `F⍥G` if used dyadically:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 207,
+ "id": "streaming-blind",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "36\n",
+ ""
+ ]
+ },
+ "execution_count": 207,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍳10) +⍥≢ ⎕A"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 208,
+ "id": "apart-hartford",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "36\n",
+ ""
+ ]
+ },
+ "execution_count": 208,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍳10) ((≢⊣)+(≢⊢)) ⎕A"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 209,
+ "id": "proprietary-measure",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "36\n",
+ ""
+ ]
+ },
+ "execution_count": 209,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍳10) (≢⍤⊣+≢⍤⊢) ⎕A"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "loose-andorra",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 12***:\n",
+ "\n",
+ "`G _B_ F` used dyadically gives `⍺ G _B_ F ⍵ ←→ (G ⍺) F ⍵`, which is somewhat symmetric to `F∘G ←→ ⍺ F (G ⍵)`.\n",
+ "Thus, if the fork for _beside_ was `(⊣FG⍤⊢)` we get that the fork for _behind_ is `(G⍤⊣F⊢)`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 210,
+ "id": "caring-brush",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 210,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "¯2 |_B_+ 5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 211,
+ "id": "anticipated-drunk",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "7\n",
+ ""
+ ]
+ },
+ "execution_count": 211,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "¯2 (|⍤⊣+⊢) 5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "basic-counter",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 13***:\n",
+ "\n",
+ "In the case of the first dfn `{|×⍵}`, it is so short that we realise `(|×)` is just an atop:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "heavy-radius",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(|×) ¯3.14"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "associate-prior",
+ "metadata": {},
+ "source": [
+ "When rewriting a dfn or another expression as a function train, arrays (whether the argument or constant arrays used in the expression) have to go in the odd positions.\n",
+ "Dyadic functions that combine results that are computed from the argument tend to be the functions that go in the even positions.\n",
+ "\n",
+ "For example, in the dfn `{*2×⍵}` we see the arrays `2` and `⍵` and we could try to put them in the odd positions of a train: `(... _ 2 _ ⊢)`.\n",
+ "We use the function _right tack_ instead of `⍵` because, in tacit programming we do not mention the arguments explicitly.\n",
+ "Then, we see that the function _times_ is a dyadic function combining the `2` and the `⍵`, so that can be the centre function in the fork of the first three functions: `(... _ 2 × ⊢)`.\n",
+ "Finally, we see that the function _exponential_ is applied atop the result, so the train becomes `(*2×⊢)`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "vanilla-kenya",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "54.5982\n",
+ ""
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(*2×⊢) 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "discrete-palestinian",
+ "metadata": {},
+ "source": [
+ "Similarly, in the dfn `{2+5×⍵}`, we see three arrays and no computations are done to the left of the `2`, so we can expect to try and fit that dfn in a train that looks like `(2 _ 5 _ ⊢)`.\n",
+ "Conveniently enough, it is enough to insert the same functions in the same positions, and the train is correct:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "anonymous-italian",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "¯13.7\n",
+ ""
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(2+5×⊢) ¯3.14"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "diverse-terrain",
+ "metadata": {},
+ "source": [
+ "The same reasoning works for the dfns `{⍵-2*⍵}` and `{|⍵-2*⍵}`, giving the trains below:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "signal-translation",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "¯0.914214\n",
+ ""
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⊢-2*⊢) 0.5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "competent-header",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "0.914214\n",
+ ""
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(|⊢-2*⊢) 0.5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "worthy-clear",
+ "metadata": {},
+ "source": [
+ "In the dfn `{(⍵*2)+⍵*3}`, we see that the function `+` is combining the results of computing `⍵*2` and `⍵*3`, thus `+` seems like the centre function of a fork `(F + G)`.\n",
+ "Now, we need `F` to compute `⍵*2` and we need `G` to compute `⍵*3`.\n",
+ "\n",
+ "If you look at the expression `⍵*2` as being the function _square_ applied to `⍵`, and if you look at the expression `⍵*3` as the function _cube_ applied to `⍵`, then the fork `(*∘2+*∘3)` might look more natural to you."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "allied-toolbox",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "12\n",
+ ""
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(*∘2+*∘3) 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "descending-controversy",
+ "metadata": {},
+ "source": [
+ "If you start to associate the argument(s) with the functions _left_ and _right tack_, you might see `⍵*2` as the \"fork\" `(⊢*2)`, except that forks cannot have arrays on the right.\n",
+ "You could fix this by writing `(2*⍨⊢)` or `(⊢*2⍨)`.\n",
+ "Thus, you would come up with a fork like `((2*⍨⊢)+(3*⍨⊢))`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "adapted-synthetic",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "12\n",
+ ""
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "((2*⍨⊢)+(3*⍨⊢)) 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "metallic-retirement",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 14***:\n",
+ "\n",
+ "In trains that are used dyadically, whenever we find a function that is being used with both the left and right arguments, that is a strong indication that that function should be in an odd position of a train.\n",
+ "For example, for the first dfn `{÷⍵-⍺}`, we see the function _minus_ being applied to both arguments, albeit in the wrong order.\n",
+ "This means that `(... -⍨)` is a good start for our train.\n",
+ "Then, we can finish our train with the function _reciprocal_ atop the subtraction:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "dominican-somalia",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1\n",
+ ""
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "3 (÷-⍨) 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "consecutive-yesterday",
+ "metadata": {},
+ "source": [
+ "Likewise, the dfn `{(⍺+⍵)×⍺-⍵}` shows the functions _plus_ and _minus_ being used dyadically, hinting at a fork of the form `(+ ... -)`.\n",
+ "Then, it is just a matter of inserting the function _times_ in the centre:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "id": "technological-material",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "¯7\n",
+ ""
+ ]
+ },
+ "execution_count": 23,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "3 (+×-) 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "specified-helmet",
+ "metadata": {},
+ "source": [
+ "Again, we see two functions being used dyadically with `⍺` and `⍵`, that are likely to go in odd positions.\n",
+ "On top of that, we see the array `1` being used explicitly.\n",
+ "Constant arrays also go in odd positions, so we have a possible train structure that looks like `(1 _ + _ -)`.\n",
+ "Then, we fill in the gaps and arrive at the train below:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "id": "informed-research",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "¯6\n",
+ ""
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "3 (1++×-) 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "threaded-literacy",
+ "metadata": {},
+ "source": [
+ "In dyadic trains where one of the arguments shows up isolated, that is likely to be a place to use a _left_ or _right tack_, depending on whether we need `⍺` or `⍵`, respectively.\n",
+ "Below, by inspecting the positions of the arguments and the literal arrays, we can suspect our train will have a structure like `(⊢ _ 1 _ ⊣)`.\n",
+ "To finish this train, we insert the functions that are missing:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "id": "verbal-respect",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "256\n",
+ ""
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "3 (⊢*1+⊣) 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "extended-crowd",
+ "metadata": {},
+ "source": [
+ "When part of the expression that is being translated contains a function that is applied to only one of the arguments, we must work around that in some way.\n",
+ "For example, in the dfn `{(⍳⍵)*1+⍺}`, we can modify the train `(⊢*1+⊣)` by having the _index generator atop the right tack_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "id": "blessed-appearance",
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "1 16 81 256\n",
+ ""
+ ]
+ },
+ "execution_count": 26,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "3 (⍳⍤⊢*1+⊣) 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "split-edwards",
+ "metadata": {},
+ "source": [
+ "In the example of the dfn `{⍵*1+⍳⍺}` we can do the exact same thing to apply the _index generator atop the left tack_, resulting in the train"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "id": "cubic-content",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "16 64 256\n",
+ ""
+ ]
+ },
+ "execution_count": 30,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "3 (⊢*1+⍳⍤⊣) 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "seasonal-joseph",
+ "metadata": {},
+ "source": [
+ "Another alternative would be to use the operator _beside_ so that the function _plus_ becomes _plus with the right argument pre-processed by the index generator_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "id": "nearby-seventh",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "16 64 256\n",
+ ""
+ ]
+ },
+ "execution_count": 31,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "3 (⊢*1+∘⍳⊣) 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "driving-polyester",
+ "metadata": {},
+ "source": [
+ "(Note that, in the prose above, the \"right argument\" of _plus_ that is pre-processed is actually the left argument of the train.)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "instrumental-minority",
+ "metadata": {},
+ "source": [
+ "\n",
+ "***Solution 15***:\n",
+ "\n",
+ "The dfn `{,1↑⍵+⍉⍵}` takes a square numeric matrix, adds it with its transpose, and then returns the first row of the resulting matrix as a vector.\n",
+ "Here is an example:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "id": "expected-proxy",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 8 14 20 26\n",
+ ""
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "{,1↑⍵+⍉⍵} 5 5⍴⍳25"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "placed-zealand",
+ "metadata": {},
+ "source": [
+ "Of the seven trains shown, the only one that is not equivalent to this dfn is the second option, `(,∘1∘↑⊢+⍉)`.\n",
+ "\n",
+ "The second option is not equivalent to the dfn because of the derived function `,∘1∘↑` that is applied atop the fork `(⊢+⍉)`.\n",
+ "The derived function `,∘1∘↑` is parsed as `(,∘1)∘↑` because operators bind from the left.\n",
+ "Thus, after we add the matrix to its transpose, we mix the matrix (which leaves it unchanged) and then we catenate the scalar `1` to its right, adding a column of ones:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "id": "changed-equipment",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " 2 8 14 20 26 1\n",
+ " 8 14 20 26 32 1\n",
+ "14 20 26 32 38 1\n",
+ "20 26 32 38 44 1\n",
+ "26 32 38 44 50 1\n",
+ ""
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(,∘1∘↑⊢+⍉) 5 5⍴⍳25"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "naughty-attendance",
+ "metadata": {},
+ "source": [
+ "Now, we explain why the other trains are equivalent to the dfn.\n",
+ "The very first one, `(,1↑⊢+⍉)` is a direct translation of the dfn:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "id": "protective-copyright",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 8 14 20 26\n",
+ ""
+ ]
+ },
+ "execution_count": 50,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(,1↑⊢+⍉) 5 5⍴⍳25"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "unique-cement",
+ "metadata": {},
+ "source": [
+ "The third one is `(,⍉1∘↑⍤+⊢)`, which is pretty similar to the first one, except that the part \"take the first row\" was moved atop the addition of the matrix argument and its transpose.\n",
+ "This can be seen in the tree diagram of the train:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "id": "ruled-oakland",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┴─┐\n",
+ ", ┌─┼─┐\n",
+ " ⍉ ⍤ ⊢\n",
+ " ┌┴┐\n",
+ " ∘ +\n",
+ " ┌┴┐\n",
+ " 1 ↑\n",
+ ""
+ ]
+ },
+ "execution_count": 52,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(,⍉1∘↑⍤+⊢)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "portable-ending",
+ "metadata": {},
+ "source": [
+ "This means that, after we add the matrix argument with its transpose, we immediately _take_ the first row.\n",
+ "Then, the function _ravel_ that is atop the fork `(⍉1∘↑⍤+⊢)` turns that 1-row matrix into the result vector:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "id": "forbidden-requirement",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 8 14 20 26\n",
+ ""
+ ]
+ },
+ "execution_count": 55,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(,⍉1∘↑⍤+⊢) 5 5⍴⍳25"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "aquatic-style",
+ "metadata": {},
+ "source": [
+ "The fourth train is `(,1↑+∘⍉⍨)`, which is a 4-train:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "id": "dental-sudan",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "┌─┴─┐\n",
+ ", ┌─┼─┐\n",
+ " 1 ↑ ⍨\n",
+ " ┌─┘\n",
+ " ∘\n",
+ " ┌┴┐\n",
+ " + ⍉\n",
+ ""
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(,1↑+∘⍉⍨)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "north-multimedia",
+ "metadata": {},
+ "source": [
+ "What we need to do is understand the rightmost function in the train, which is `+∘⍉⍨`.\n",
+ "The train is called monadically, so the function `+∘⍉⍨` will also be called monadically, which means the operator `⍨` is the operator _selfie_: `+∘⍉⍨ ⍵ ←→ ⍵ +∘⍉ ⍵`.\n",
+ "Now, the usage of the operator _beside_ means that we pre-process the right argument of _plus_ with _transpose_, so we end up with `+∘⍉⍨ ⍵ ←→ ⍵ +∘⍉ ⍵ ←→ ⍵ + ⍉⍵`, which is exactly what we have in the dfn:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 54,
+ "id": "improved-winning",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 8 14 20 26\n",
+ ""
+ ]
+ },
+ "execution_count": 54,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(,1↑+∘⍉⍨) 5 5⍴⍳25"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "metropolitan-software",
+ "metadata": {},
+ "source": [
+ "The fifth and sixth trains, `((,1∘↑)⍉+⊢)` and `(,∘(1∘↑)⊢+⍉)`, differ in two things.\n",
+ "The first difference is in the first three functions: `(⍉+⊢)` versus `(⊢+⍉)`.\n",
+ "However, _plus_ is a commutative function, so this difference in the definition of the train doe not affect the result.\n",
+ "\n",
+ "The second difference is in the way the leftmost function is defined.\n",
+ "Notice how both trains are 4-trains.\n",
+ "\n",
+ "In one, we have a nested 2-train `(,1∘↑)` atop the fork.\n",
+ "This atop applies the functions _take one_ and _ravel_ consecutively to the addition of the matrix argument with its transpose.\n",
+ "\n",
+ "In the other, we have the derived function `,∘(1∘↑)` atop the fork.\n",
+ "This derived function uses parentheses to prevent `,∘1∘↑` to bind from the left, like in the second train.\n",
+ "\n",
+ "In short, both trains are equivalent to the original dfn:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "id": "legendary-whale",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 8 14 20 26\n",
+ ""
+ ]
+ },
+ "execution_count": 56,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "((,1∘↑)⍉+⊢) 5 5⍴⍳25"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "id": "religious-wrong",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 8 14 20 26\n",
+ ""
+ ]
+ },
+ "execution_count": 57,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(,∘(1∘↑)⊢+⍉) 5 5⍴⍳25"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "found-brazil",
+ "metadata": {},
+ "source": [
+ "The final train that we have to study is `(≢↑⍉,⍤+⊢)`.\n",
+ "This one looks more different from the original dfn because it uses primitive functions that are not present in the original dfn.\n",
+ "\n",
+ "This train is a 5-train that starts with the fork `(⍉,⍤+⊢)`.\n",
+ "This fork starts by adding the argument matrix to its transpose and then ravels it, because of the function _ravel_ atop the function _plus_:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "id": "gentle-ballot",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ " 2 8 14 20 26\n",
+ " 8 14 20 26 32\n",
+ "14 20 26 32 38\n",
+ "20 26 32 38 44\n",
+ "26 32 38 44 50\n",
+ ""
+ ]
+ },
+ "execution_count": 59,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍉+⊢) 5 5⍴⍳25"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "id": "alone-writer",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 8 14 20 26 8 14 20 26 32 14 20 26 32 38 20 26 32 38 44 26 32 38 44 50\n",
+ ""
+ ]
+ },
+ "execution_count": 60,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(⍉,⍤+⊢) 5 5⍴⍳25"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "intellectual-contest",
+ "metadata": {},
+ "source": [
+ "Then, that ravel is going to be the right argument to the fork `(≢↑⊢)`.\n",
+ "The function _tally_ takes the original matrix as argument, so the result of that will be the number of rows (or columns, because it is a square matrix) that the matrix has.\n",
+ "Thus, from the ravel of the matrix addition, we will _take_ the number of elements of a single row, which corresponds to the first row of the matrix addition:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "id": "increasing-marina",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "2 8 14 20 26\n",
+ ""
+ ]
+ },
+ "execution_count": 61,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "(≢↑⍉,⍤+⊢) 5 5⍴⍳25"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Dyalog APL",
+ "language": "apl",
+ "name": "dyalog-kernel"
+ },
+ "language_info": {
+ "file_extension": ".apl",
+ "mimetype": "text/apl",
+ "name": "APL"
+ },
+ "toc": {
+ "base_numbering": 1,
+ "nav_menu": {},
+ "number_sections": false,
+ "sideBar": true,
+ "skip_h1_title": true,
+ "title_cell": "Table of Contents",
+ "title_sidebar": "Contents",
+ "toc_cell": false,
+ "toc_position": {
+ "height": "calc(100% - 180px)",
+ "left": "10px",
+ "top": "150px",
+ "width": "384px"
+ },
+ "toc_section_display": true,
+ "toc_window_display": true
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/res/Atop_Diagram.svg b/res/Atop_Diagram.svg
new file mode 100644
index 0000000..7381fe4
--- /dev/null
+++ b/res/Atop_Diagram.svg
@@ -0,0 +1,53 @@
+
\ No newline at end of file
diff --git a/res/Beside_Diagram.svg b/res/Beside_Diagram.svg
new file mode 100644
index 0000000..5fcf588
--- /dev/null
+++ b/res/Beside_Diagram.svg
@@ -0,0 +1,53 @@
+
\ No newline at end of file
diff --git a/res/Compositional_Operators_Comparison.svg b/res/Compositional_Operators_Comparison.svg
new file mode 100644
index 0000000..009222a
--- /dev/null
+++ b/res/Compositional_Operators_Comparison.svg
@@ -0,0 +1,73 @@
+
\ No newline at end of file
diff --git a/res/Over_Diagram.svg b/res/Over_Diagram.svg
new file mode 100644
index 0000000..cc8d0a5
--- /dev/null
+++ b/res/Over_Diagram.svg
@@ -0,0 +1,56 @@
+
\ No newline at end of file