Dyalog
Higher-order array reductions eliminate explicit recursion branching by coupling dynamic subtree generation directly into accumulator folds, pruning exponential search spaces to viable solutions.
AI-assisted summary of Depth-first search in APL — original source. Check the original for full context.
Source published:
Key ideas
- A general two-operand depth-first search operator in APL decouples accumulation logic (left operand) from subtree projection logic (right operand), enabling recursion without explicit base-case checks. (1:30)
- The tree traversal pattern seamlessly spans concrete nested arrays and just-in-time dynamic state trees generated on the fly, such as in game search spaces. (5:28)
- Applying DFS to the 8-queens problem cuts the search space from an unconstrained 8^8 (16,777,216) candidate states down to only 2,057 visited nodes. (8:23)
- Valid N-queens solution counts across board dimensions N = 0 through 10 are computed directly as 1, 1, 0, 0, 2, 10, 4, 40, 92, 352, and 724. (8:23)
Detailed notes
## Background Published by Dyalog on YouTube on July 18, 2014, this video presents a step-by-step development of a general depth-first tree-search operator within an interactive APL session. The show notes highlight that the conceptual material is concentrated and recommend pausing periodically to absorb the code.
## TL;DR The tutorial demonstrates how to design a general, higher-order depth-first search operator in Dyalog APL that folds an accumulator across tree nodes without requiring explicit base-case branching. After demonstrating traversals on static nested arrays to count nodes or extract leaves, the presentation shifts to generating subtrees dynamically just in time. Using the classic N-Queens problem, it calculates diagonal and vertical attack vectors to prune illegal placements on the fly, dramatically cutting the 8-queens search space from over 16 million potential states down to 2,057 visited nodes before formatting the resulting solutions into visual chessboard matrices.
## Atmosphere & style The presentation is quiet, cerebral, and intensely focused, characterized by the deliberate, methodical cadence of a live programming session. It carries an elegant, almost mathematical clarity as dense APL primitives are introduced and composed into expressive higher-order operators. Watching it feels like a sequence of satisfying conceptual reveals, shifting smoothly from abstract operator theory to practical algorithmic problem-solving.
## Bulletin - [01:30](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=90s) A general two-operand depth-first search operator in APL decouples accumulation logic (left operand) from subtree projection logic (right operand), enabling recursion without explicit base-case checks. - [05:28](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=328s) The tree traversal pattern seamlessly spans concrete nested arrays and just-in-time dynamic state trees generated on the fly, such as in game search spaces. - [08:23](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=503s) Applying DFS to the 8-queens problem cuts the search space from an unconstrained 8^8 (16,777,216) candidate states down to only 2,057 visited nodes. - [08:23](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=503s) Valid N-queens solution counts across board dimensions N = 0 through 10 are computed directly as 1, 1, 0, 0, 2, 10, 4, 40, 92, 352, and 724.
## Key takeaways - [00:07](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=7s) Depth-first search visits a root node and recursively traverses each subtree in sequence. - [00:07](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=7s) Dyalog APL distinguishes operands from arguments using double symbols (⍶, ⍹) for operands and single symbols (⍺, ⍵) for array arguments. - [01:30](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=90s) The DFS operator accepts an initial accumulator (left argument) and a tree (right argument), pairing an accumulating function (left operand) with a subtree expansion function (right operand). - [01:30](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=90s) Recursive del (∇) combined with commute (⍨) and right-to-left reduction folds tree state without requiring conditional base-case testing. - [03:38](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=218s) The right operand alone determines the tree's structure and semantics, allowing any arbitrary data representation to be interpreted as a tree. - [03:38](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=218s) Currying the search operator with a static right operand derives specialized single-operand operators for node counting, leaf counting, or leaf extraction. - [05:28](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=328s) Subtrees can be synthesized just in time rather than stored statically, making DFS suitable for combinatorial puzzle solving. - [05:28](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=328s) In N-queens, column attacks across vertical, left diagonal, and right diagonal lines are generated by scaling direction vectors (¯1, 0, 1) against placement depths. - [08:23](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=503s) Pruning illegal queen placements during dynamic subtree generation reduces the 8-queens search space to 2,057 visited nodes versus 8^8 total positions. - [08:23](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=503s) Valid solutions to the N-queens problem correspond precisely to leaf nodes whose placement vectors match board dimension N. - [08:23](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=503s) Placement vectors can be transformed into visual chessboard matrices using outer product equality (∘.=) with row indices.
## Detailed walkthrough ### [00:07](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=7s) Higher-Order Operators and the Mechanics of Depth-First Search
A sample tree is introduced containing 12 vertices (nodes), consisting of a single root and seven leaves. As the narrator explains:
> “A depth first search or traversal visits the root node and then recursively traverses each subtree in turn.”
APL provides higher-order operators that accept function operands in addition to regular array arguments. Standard primitive operators illustrate this capability: - Reduction (fold), such as product reduction (`×/`) - Each (map), such as reversing each vector in a vector of vectors (`⌽¨`) - Commute (switch), which reverses operand order (e.g., `1 2,⍨ 3 4`)
When writing user-defined operators in Dyalog APL, distinct syntax differentiates operands from arguments: `⍶` (double alpha) and `⍹` (double omega) refer to the left and right function operands, respectively, while `⍺` (single alpha) and `⍵` (single omega) designate the left and right array arguments. To demonstrate, an operator is defined that applies its left operand function twice to its right array argument (`⍺⍺ ⍺⍺ ⍵`), demonstrated by applying enclose (`⊂`) twice to a simple character vector.
---
[01:30](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=90s) Designing the Depth-First Search Operator in APL
The video sets out to construct a general two-operand depth-first search operator, using an informal type notation to formalize the design intent:
> “Depth first search will be an operator which takes an initial accumulator value as left argument and a tree as right argument and returns an accumulated value for the whole tree with a left operand function which takes an accumulator value and a tree node and returns a successor value and a right operand function which takes an accumulator value and a tree node and returns a possibly null vector of subtrees.”
In the implementation, the left operand function `⍶` visits the current node `⍵`, producing a successor to the accumulator value `⍺`. This resulting accumulator is enclosed into a single array element and prepended to the vector of subtrees generated by the right operand function `⍹`. Following the node visit, the search proceeds recursively. The del symbol (`∇`) denotes the derived operator bound with its operand functions, applying an accumulating reduction across the value for the node and each of its child subtrees. Because APL's primitive reduction operator folds right-to-left, the argument vector is reversed and the arguments of the recursive call are commuted (`⍨`). Finally, disclose (`⊃`) unpacks the accumulated result.
```mermaid flowchart TD accTitle: Depth-First Search Operator Execution Flow accDescr: Diagram illustrating how the DFS operator processes an accumulator and node, computes subtrees, and folds recursively.
subgraph Step1 ["1. Visit Current Node"] A["Input: Accumulator ⍺ and Node ⍵"] --> B["Compute Subtrees: ⍺ ⍹ ⍵"] A --> C["Compute Successor: ⊂(⍺ ⍶ ⍵)"] end
subgraph Step2 ["2. Combine and Recurse"] C --> D["Prepend Node Value to Subtrees"] B --> D D --> E["Reverse Argument Vector: ⌽"] E --> F["Recursive Del Fold: ∇⍨/"] end
subgraph Step3 ["3. Return Result"] F --> G["Disclose Final Accumulator: ⊃"] end ```
The operator requires no branching or explicit base-case check: > “We don't need an explicit test for the base case of the recursion if the vector of sub trees is empty the reduction is applied to a single item the value for a leaf node which it just returns as result.”
---
[03:38](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=218s) Traversing Static Nested Array Trees
The operator is tested against a concrete nested vector representation containing seven nodes: one root, two intermediate branches, and four leaves. To search this static structure, the right operand function must inspect the current node `⍵` and return a vector of its subtrees: - If the node's array depth is zero (`0 = ≡⍵`), it is a leaf, returning an empty vector. - Otherwise, it drops the first element to expose the child subtrees (`1↓⍵`).
As the narrator highlights:
> “It is the right operand function alone that projects a tree type onto the subject array.”
By currying the search operator with this right operand, a derived one-operand operator is formed that traverses that specific tree layout while accepting arbitrary accumulating functions as its left operand: - **Counting total nodes**: Initializing the accumulator to `0` with an addition function incrementing by 1 at each step (`{⍺+1}`). - **Counting leaves only**: Incrementing only when the array depth is zero (`{⍺ + 0=≡⍵}`). - **Enlisting leaves**: Initializing with an empty vector (`⍬`) and joining leaf items (`{⍺, (0=≡⍵)/⍵}`).
In all of these cases, excluded nodes supply an identity value (`0` for addition, `⍬` for joining). While flattening nested arrays is directly supported by APL's primitive enlist operator (`∊`), this general formulation shows how custom tree traversals emerge naturally from higher-order operators.
---
[05:28](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=328s) Realizing Trees Dynamically: The N-Queens Problem and Attack Vectors
Rather than traversing a pre-existing in-memory array, subtrees can be synthesized on demand during traversal:
> “The tree can be either concrete above or sub trees can be realized just in time by the right operand function for example when tree searching the solution to a problem such as Sudoku or a chess puzzle.”
To illustrate dynamic tree expansion, the classic eight queens problem is introduced, where eight queens must be positioned on a chessboard so that none attacks another. Working row by row, queen placements are represented as vectors of column indices. For example, three queens placed a knight's move apart in the first three rows produce attack vectors into the fourth row along three directions: - **Right diagonals**: Calculated using the reverse indices of the placement vector's shape (`⌽⍳⍴⍵`). - **Left diagonals**: Offset in the opposite direction (where out-of-bounds positions like column 0 fall off the board). - **Straight ahead**: Directly attacking the occupied column indices (`0×...`).
These directions are captured systematically by scaling an index range across direction offsets `¯1`, `0`, and `1`: - Each direction vector scales the row distance (`¯1 0 1 ר ⊂⌽⍳⍴⍵`). - Adding the current queen positions (`+¨ ⊂⍵`) produces all threatened column coordinates. - Flattening (`∊`) and deduplicating (`∪`) these coordinates yields an `attack` function.
The available columns for placing the next queen in row 4 are simply all board columns except those under attack. Appending each valid candidate column to the current placement vector (`⍵,¨...`) yields the valid successor placements. This function serves directly as the right operand `⍹` to generate subtrees just in time.
---
[08:23](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=503s) Solving N-Queens: Node Counts, Leaf Solutions, and Matrix Display
The formulation is generalized from an 8×8 board to arbitrary $N \times N$ boards by using the length of the first item in the accumulator to determine board dimension $N$. Binding DFS with this dynamic subtree generator creates a problem-specific tree search operator.
Searching an 8×8 board from an empty placement vector (`⍬`) and counting visited nodes using a length-8 zero accumulator reveals the size of the pruned search space:
> “Compare this number with the tree size for all possible placements of eight Queens 8 to the power 8.”
The DFS search explores only 2,057 nodes, compared to the unconstrained combinatorial space of $8^8 = 16,777,216$ positions. Valid solutions reside at the leaves of the tree where the placement vector length equals $N$: - Board sizes $N = 0$ through $10$ yield solution counts of: `1, 1, 0, 0, 2, 10, 4, 40, 92, 352, 724`.
To collect the placements rather than just counting them, the accumulator starts as an empty vector and appends matching paths when row count equals $N$. A 4×4 board produces two distinct solutions. To make the output visually recognizable, each placement vector is formatted into a Boolean chessboard matrix via outer product equality with row indices (`∘.=`).
Wrapping these operations together produces a concise `nqueens` function capable of generating and formatting valid board configurations:
> “Let's pause for a moment to contemplate the code before going to experiment at tryapl.org.”
## Notable quotes > [03:38](https://www.youtube.com/watch?v=DsZdfnlh_d0&t=218s) It is the right operand function alone that projects a tree type onto the subject array.
## Mentioned APL, Dyalog, Dyalog APL, tryapl.org, N-Queens problem, Sudoku
## Video description Step-by-step development of a depth-first tree-search operator in an APL session. NB: The concepts in this little video are fairly concentrated; it may help to pause by hitting the SPACE BAR now and again.
--------------------
GET STARTED
Download the latest version of Dyalog: https://www.dyalog.com/downloads
TAKE PART IN OUR APL COMPETITIONS
THE APL CHALLENGE Learn APL and compete for one of three $100 prizes. Open to all experience levels. https://challenge.dyalog.com
THE APL FORGE Got a problem to solve or a library to share? Use Dyalog APL to build something great — and win £2,500 + an expenses-paid trip to present at a Dyalog user meeting. https://forge.dyalog.com
--------------------
[Source](https://www.youtube.com/watch?v=DsZdfnlh_d0)