aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarshall Lochbaum <mwlochbaum@gmail.com>2021-07-14 21:10:09 -0400
committerMarshall Lochbaum <mwlochbaum@gmail.com>2021-07-14 21:10:09 -0400
commitf113d9f57bdae219c87887c5e0781a5c824dc8e4 (patch)
treef51c1a7b220d8e144d15f79307aaddb4e0c43896
parentc64b7fc606f3a78f6893298d33f272c7323200ab (diff)
Document search functions on lists
-rw-r--r--doc/README.md1
-rw-r--r--doc/primitive.md6
-rw-r--r--doc/search.md73
-rw-r--r--docs/doc/index.html1
-rw-r--r--docs/doc/primitive.html8
-rw-r--r--docs/doc/search.html72
6 files changed, 148 insertions, 13 deletions
diff --git a/doc/README.md b/doc/README.md
index 1a051b24..e3736ac2 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -49,6 +49,7 @@ Primitives:
- [Repeat](repeat.md) (`⍟`)
- [Reverse and Rotate](reverse.md) (`⌽`)
- [Scan](scan.md) (`` ` ``)
+- [Search functions](search.md) (`⊐⊒∊`)
- [Select](select.md) (`⊏`)
- [Self-comparison functions](selfcmp.md) (`⊐⊒∊⍷`)
- [Shift functions](shift.md) (`»«`)
diff --git a/doc/primitive.md b/doc/primitive.md
index c04a4760..b768be37 100644
--- a/doc/primitive.md
+++ b/doc/primitive.md
@@ -51,9 +51,9 @@ Functions that have significant differences from APL functions are marked with a
| `⍒` | [Grade Down](order.md#grade) | [Bins Down](order.md#bins)
| `⊏` | [First Cell](select.md)* | [Select](select.md)*
| `⊑` | [First](pick.md#first) | [Pick](pick.md)*
-| `⊐` | [Classify](selfcmp.md#classify)* (`⍷⊸⊐`) | [Index of](https://aplwiki.com/wiki/Index_Of)
-| `⊒` | [Occurrence Count](selfcmp.md#occurrence-count)* | Progressive Index of*
-| `∊` | [Mark Firsts](selfcmp.md#mark-firsts) | [Member of](https://aplwiki.com/wiki/Membership)
+| `⊐` | [Classify](selfcmp.md#classify)* | [Index of](search.md#index-of)
+| `⊒` | [Occurrence Count](selfcmp.md#occurrence-count)* | [Progressive Index of](search.md#progressive-index-of)*
+| `∊` | [Mark Firsts](selfcmp.md#mark-firsts) | [Member of](search.md#member-of)
| `⍷` | [Deduplicate](selfcmp.md#deduplicate) | [Find](https://aplwiki.com/wiki/Find)
| `⊔` | [Group Indices](group.md)* | [Group](group.md)*
| `!` | [Assert](assert.md)* | [Assert with Message](assert.md)*
diff --git a/doc/search.md b/doc/search.md
index a71b0676..d186e434 100644
--- a/doc/search.md
+++ b/doc/search.md
@@ -17,20 +17,27 @@ ig ← "fill=currentColor|font-size=12|opacity=0.75"
li‿lf ← ≠¨ it‿ft ← '''(Highlight∾∾⊣)¨¨"searches"‿"essays"
Text ← ("text" Attr "dy"‿"0.32em"∾(Pos d⊸×))⊸Enc
-Line ← "line" Elt ("xy"≍⌜"12")≍˘○⥊ ·FmtNum d⊸×
Rp ← Pos⊸∾⟜("width"‿"height"≍˘FmtNum)○(d⊸×)
tx ← ↕li ⋄ y ← » yd ← +`2‿1.4‿1‿1‿1.8
dim ← ⟨1.5+li,¯1⊑yd⟩ ⋄ sh ← ¯1.8‿¯1
tp ← y ≍˜¨¨ 1‿4/⟨tx,↕lf⟩
hp ← 0.2‿¯0.45(+⟜(1‿0×sh)≍¯2⊸×⊸+)1‿0×dim
-LL ← Line ·⌽˘ (≍˘⟜-0.08×4≍˜×∘-˜´) + ≍⟜(2↑y)
+L0 ← ("xy"≍⌜"12")≍˘○⥊ ·FmtNum d × ·⌽˘ (≍˘⟜-0.08×4≍˜×∘-˜´) + ≍⟜(2↑y)
+LL ← "line" Elt =⟜li "mask"‿"url(#m)"⊸∾⍟⊣ L0∘≍
Ilg← (1⊸+∾-)∘= <⊸(⊔¨) ∾≍○<∾○(↕∘≠)
-((∾˜d)×((-∾+˜)0.8‿0.3)+sh∾dim) SVG g Ge ⟨
+lgg ← "linearGradient"At"id=grad|x2=0|y2=1"
+Stop ← "stop" Elt "offset"‿"stop-color"≍˘≍○<
+defs ← "defs" Enc ("mask"At"id=m") Enc ⟨
+ lgg Enc "0.6"‿"0.9" Stop¨ "#000"‿"#fff"
+ "rect" Elt "fill"‿"url(#grad)" ∾ ((sh⊑⊸≍⊑) Rp dim⊑⊸≍-˜´) 2↑y
+⟩
+
+((∾˜d)×((-∾+˜)0.8‿0.3)+sh∾dim) SVG defs ∾ g Ge ⟨
"rect" Elt rc ∾ sh Rp dim
hg Ge ("rect" Elt ·Rp˝ {𝕩⊸+⌾(1⊑⊏)hp})¨ 2‿4⊏y
- lg Ge lgs Ge¨ LL∘≍¨¨´ it (⊐Ilg⊒) ft
+ lg Ge lgs Ge¨ LL¨¨´ it (⊐Ilg⊒) ft
ig Ge (-⟜0‿0.48¨⊑tp) Text¨ •Repr¨ tx
(∾tp) Text¨ it ∾ ft ∾ Highlight∘•Repr¨ ∾ {it 𝕏 ft}¨ ⟨⊐,⊒,∊˜⟩
cg Ge (¯0.7≍¨y) Text⟜Highlight¨ "in"‿"for"∾⥊¨"⊐⊒∊"
@@ -49,3 +56,61 @@ The three search functions are Index of (`⊐`), Progressive Index of (`⊒`), a
The searched-for argument is `𝕩` in Index-of functions (`⊐⊒`) and `𝕨` in Member of (`∊`). [Bins](order.md#bins) Up and Down (`⍋⍒`) are ordering functions but follow the same pattern as Index-of. It's split into cells, but not necessarily *major* cells: instead, the cells used match the rank of a major cell of the other (searched-in) argument. In the most common case, when the searched-in argument is a list, 0-cells are used for the search (we might also say elements, as it gives the same result).
The result is always an array containing one number for each searched-for cell. For Index of and Member of, every result is computed independently; for Progressive Index of the result for a cell can depend on earlier cells, in index order.
+
+## Member of
+
+The simplest of the search functions, Member of (`∊`) returns `1` if an entry in `𝕨` matches some entry in `𝕩`, and `0` if it doesn't.
+
+ "green"‿"bricks"‿"cow"‿"blue" ∊ "red"‿"green"‿"blue"
+
+The result is independent of the ordering of `𝕩`: all that matters is which cells it contains.
+
+Member of can be used in a [train](train.md) to compute the set intersection and difference of two arrays. For example, `∊/⊣` uses `𝕨∊𝕩` to [filter](replicate.md) `𝕨` (from `𝕨⊣𝕩`), giving an intersection.
+
+ "initial set" (∊/⊣) "intersect" # Keep 𝕩
+
+ "initial set" (¬∘∊/⊣) "difference" # Remove 𝕩
+
+These are the APL functions Intersect (`∩`) and Without (`~`). Really, only `𝕩` is treated like a set, while the ordering and multiplicity of elements of `𝕨` are maintained. I think the explicit implementations show this well, since `𝕩` is only used as the right argument to `∊`, and prefer this clarity to the brevity of a single symbol.
+
+## Index of
+
+Index of (`⊐`) returns the index of the first occurrence of each entry in `𝕨`, or `≠𝕨` if an entry doesn't appear in `𝕨` at all.
+
+ "zero"‿"one"‿"two"‿"three" ⊐ "one"‿"eight"‿"two"
+
+`𝕩∊𝕨` is the same as `(𝕨⊐𝕩)<≠𝕨`. Note the reversal of arguments! In both `∊` and `⊐`, the open side points to the searched-in argument and the closed side points to the searched-for argument. Relatedly, in Select (`⊏`), the open side points to the selected argument, which is more like the searched-in argument in that its cells are generally accessed out of order (the searched-for argument is most like the selection result `𝕨⊏𝕩`).
+
+Index of always returns exactly one number, even if there are multiple matches, or no matches at all. To find the indices of all matches, start with [Match](match.md) [Each](map.md), then [Indices](replicate.md#indices) (I didn't mean for it to sound so repetitive! It just happened!).
+
+ / "letters" ≡¨< 'e' # Many to one
+
+ "letters" (<∘/˘≡⌜˜) "let" # Many to many
+
+## Progressive Index of
+
+Progressive Index of (`⊒`), as the name and glyph suggest, is a more sophisticated variant of Index of. Like Index of, it returns either `≠𝕨` or an index of a cell from `𝕨` that matches the given cell of `𝕩`. Unlike Index of, no index except `≠𝕨` can ever be repeated. Progressive Index of returns the index of the first *unused* match, provided there's still one left.
+
+ "aaa" ⊒ "aaaaa"
+
+ "aaabb" ⊒ "ababababab"
+
+Above we said that `𝕩∊𝕨` is `(𝕨⊐𝕩)<≠𝕨`, so that `⊐˜<≠∘⊢` is an implementation of Member of. The corresponding `⊒˜<≠∘⊢` implements *progressive* member of, that is, membership on [multisets](https://en.wikipedia.org/wiki/Multiset). So if `𝕩` contains two copies of `'a'`, only the first to instances of `'a'` in `𝕨` are considered to belong to it. And like membership is useful for set intersection and difference, progressive membership gives multiset versions of these.
+
+ "aabbcc" (⊐˜<≠∘⊢) "baa"
+
+ "aabbcc" (⊒˜<≠∘⊢) "baa"
+
+ "aabbcc" ((⊒˜=≠∘⊢)/⊣) "baa" # Multiset difference
+
+This primitive gives an interesting way to implement the [ordinals](order.md#ordinals) pattern that might be easier to understand than the APL classic `⍋⍋` (it's probably a little slower though). The idea is to use the sorted array as the left argument to `⊒`. Now the index returned for each cell is just where it ended up in that sorted order. If we used ordinary Index of then equal cells would share the smallest index; Progressive Index of means ties are broken in favor of earlier cells.
+
+ ⍋∘⍋ "adebcedba"
+
+ ∧⊸⊒ "adebcedba"
+
+ ∧⊸⊐ "adebcedba" # Ties included
+
+Here's a goofy code golf tip: if the two arguments to Progressive Index of are the same, then every cell will be matched to itself, because all the previous indices are taken but the current one does match. So `⊒˜` is the same as `↕∘≠`.
+
+ ⊒˜ "anything at all"
diff --git a/docs/doc/index.html b/docs/doc/index.html
index e32dd8f8..c1eaf56b 100644
--- a/docs/doc/index.html
+++ b/docs/doc/index.html
@@ -55,6 +55,7 @@
<li><a href="repeat.html">Repeat</a> (<code><span class='Modifier2'>⍟</span></code>)</li>
<li><a href="reverse.html">Reverse and Rotate</a> (<code><span class='Function'>⌽</span></code>)</li>
<li><a href="scan.html">Scan</a> (<code><span class='Modifier'>`</span></code>)</li>
+<li><a href="search.html">Search functions</a> (<code><span class='Function'>⊐⊒∊</span></code>)</li>
<li><a href="select.html">Select</a> (<code><span class='Function'>⊏</span></code>)</li>
<li><a href="selfcmp.html">Self-comparison functions</a> (<code><span class='Function'>⊐⊒∊⍷</span></code>)</li>
<li><a href="shift.html">Shift functions</a> (<code><span class='Function'>»«</span></code>)</li>
diff --git a/docs/doc/primitive.html b/docs/doc/primitive.html
index 46f8d444..e8eedc23 100644
--- a/docs/doc/primitive.html
+++ b/docs/doc/primitive.html
@@ -206,18 +206,18 @@
</tr>
<tr>
<td><code><span class='Function'>⊐</span></code></td>
-<td><a href="selfcmp.html#classify">Classify</a>* (<code><span class='Function'>⍷</span><span class='Modifier2'>⊸</span><span class='Function'>⊐</span></code>)</td>
-<td><a href="https://aplwiki.com/wiki/Index_Of">Index of</a></td>
+<td><a href="selfcmp.html#classify">Classify</a>*</td>
+<td><a href="search.html#index-of">Index of</a></td>
</tr>
<tr>
<td><code><span class='Function'>⊒</span></code></td>
<td><a href="selfcmp.html#occurrence-count">Occurrence Count</a>*</td>
-<td>Progressive Index of*</td>
+<td><a href="search.html#progressive-index-of">Progressive Index of</a>*</td>
</tr>
<tr>
<td><code><span class='Function'>∊</span></code></td>
<td><a href="selfcmp.html#mark-firsts">Mark Firsts</a></td>
-<td><a href="https://aplwiki.com/wiki/Membership">Member of</a></td>
+<td><a href="search.html#member-of">Member of</a></td>
</tr>
<tr>
<td><code><span class='Function'>⍷</span></code></td>
diff --git a/docs/doc/search.html b/docs/doc/search.html
index 118cd35a..285f8b46 100644
--- a/docs/doc/search.html
+++ b/docs/doc/search.html
@@ -6,6 +6,15 @@
<div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a> / <a href="../index.html">main</a> / <a href="index.html">doc</a></div>
<h1 id="search-functions">Search functions</h1>
<svg viewBox='-124.8 -46.8 532.8 280.8'>
+ <defs>
+ <mask id='m'>
+ <linearGradient id='grad' x2='0' y2='1'>
+ <stop offset='0.6' stop-color='#000'/>
+ <stop offset='0.9' stop-color='#fff'/>
+ </linearGradient>
+ <rect fill='url(#grad)' x='-86.4' y='0' width='456' height='72'/>
+ </mask>
+ </defs>
<g font-family='BQN,monospace' font-size='19px' text-anchor='middle'>
<rect class='code' stroke-width='1.5' rx='12' x='-86.4' y='-36' width='456' height='259.2'/>
<g class='purple' stroke-width='0' opacity='0.5'>
@@ -15,7 +24,7 @@
<g class='lilac' stroke-linecap='round'>
<g stroke-width='1' stroke-dasharray='6,7'>
<line x1='99.84' x2='332.16' y1='60.48' y2='11.52'/>
- <line x1='243.84' x2='380.16' y1='60.48' y2='11.52'/>
+ <line mask='url(#m)' x1='243.84' x2='380.16' y1='60.48' y2='11.52'/>
</g>
<g stroke-width='1.5'>
<line x1='92.16' x2='3.84' y1='60.48' y2='11.52'/>
@@ -25,7 +34,7 @@
<line x1='3.84' x2='44.16' y1='60.48' y2='11.52'/>
<line x1='44.16' x2='3.84' y1='60.48' y2='11.52'/>
<line x1='140.16' x2='99.84' y1='60.48' y2='11.52'/>
- <line x1='195.84' x2='380.16' y1='60.48' y2='11.52'/>
+ <line mask='url(#m)' x1='195.84' x2='380.16' y1='60.48' y2='11.52'/>
</g>
</g>
<g fill='currentColor' font-size='12' opacity='0.75'>
@@ -124,3 +133,62 @@
</table>
<p>The searched-for argument is <code><span class='Value'>𝕩</span></code> in Index-of functions (<code><span class='Function'>⊐⊒</span></code>) and <code><span class='Value'>𝕨</span></code> in Member of (<code><span class='Function'>∊</span></code>). <a href="order.html#bins">Bins</a> Up and Down (<code><span class='Function'>⍋⍒</span></code>) are ordering functions but follow the same pattern as Index-of. It's split into cells, but not necessarily <em>major</em> cells: instead, the cells used match the rank of a major cell of the other (searched-in) argument. In the most common case, when the searched-in argument is a list, 0-cells are used for the search (we might also say elements, as it gives the same result).</p>
<p>The result is always an array containing one number for each searched-for cell. For Index of and Member of, every result is computed independently; for Progressive Index of the result for a cell can depend on earlier cells, in index order.</p>
+<h2 id="member-of">Member of</h2>
+<p>The simplest of the search functions, Member of (<code><span class='Function'>∊</span></code>) returns <code><span class='Number'>1</span></code> if an entry in <code><span class='Value'>𝕨</span></code> matches some entry in <code><span class='Value'>𝕩</span></code>, and <code><span class='Number'>0</span></code> if it doesn't.</p>
+<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=ImdyZWVuIuKAvyJicmlja3Mi4oC/ImNvdyLigL8iYmx1ZSIg4oiKICJyZWQi4oC/ImdyZWVuIuKAvyJibHVlIg==">↗️</a><pre> <span class='String'>&quot;green&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;bricks&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;cow&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;blue&quot;</span> <span class='Function'>∊</span> <span class='String'>&quot;red&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;green&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;blue&quot;</span>
+⟨ 1 0 0 1 ⟩
+</pre>
+<p>The result is independent of the ordering of <code><span class='Value'>𝕩</span></code>: all that matters is which cells it contains.</p>
+<p>Member of can be used in a <a href="train.html">train</a> to compute the set intersection and difference of two arrays. For example, <code><span class='Function'>∊/⊣</span></code> uses <code><span class='Value'>𝕨</span><span class='Function'>∊</span><span class='Value'>𝕩</span></code> to <a href="replicate.html">filter</a> <code><span class='Value'>𝕨</span></code> (from <code><span class='Value'>𝕨</span><span class='Function'>⊣</span><span class='Value'>𝕩</span></code>), giving an intersection.</p>
+<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=ImluaXRpYWwgc2V0IiAo4oiKL+KKoykgImludGVyc2VjdCIgICAgICMgS2VlcCDwnZWpCgoiaW5pdGlhbCBzZXQiICjCrOKImOKIii/iiqMpICJkaWZmZXJlbmNlIiAgIyBSZW1vdmUg8J2VqQ==">↗️</a><pre> <span class='String'>&quot;initial set&quot;</span> <span class='Paren'>(</span><span class='Function'>∊/⊣</span><span class='Paren'>)</span> <span class='String'>&quot;intersect&quot;</span> <span class='Comment'># Keep 𝕩
+</span>"initiset"
+
+ <span class='String'>&quot;initial set&quot;</span> <span class='Paren'>(</span><span class='Function'>¬</span><span class='Modifier2'>∘</span><span class='Function'>∊/⊣</span><span class='Paren'>)</span> <span class='String'>&quot;difference&quot;</span> <span class='Comment'># Remove 𝕩
+</span>"tal st"
+</pre>
+<p>These are the APL functions Intersect (<code><span class='Value'>∩</span></code>) and Without (<code><span class='Value'>~</span></code>). Really, only <code><span class='Value'>𝕩</span></code> is treated like a set, while the ordering and multiplicity of elements of <code><span class='Value'>𝕨</span></code> are maintained. I think the explicit implementations show this well, since <code><span class='Value'>𝕩</span></code> is only used as the right argument to <code><span class='Function'>∊</span></code>, and prefer this clarity to the brevity of a single symbol.</p>
+<h2 id="index-of">Index of</h2>
+<p>Index of (<code><span class='Function'>⊐</span></code>) returns the index of the first occurrence of each entry in <code><span class='Value'>𝕨</span></code>, or <code><span class='Function'>≠</span><span class='Value'>𝕨</span></code> if an entry doesn't appear in <code><span class='Value'>𝕨</span></code> at all.</p>
+<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=Inplcm8i4oC/Im9uZSLigL8idHdvIuKAvyJ0aHJlZSIg4oqQICJvbmUi4oC/ImVpZ2h0IuKAvyJ0d28i">↗️</a><pre> <span class='String'>&quot;zero&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;one&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;two&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;three&quot;</span> <span class='Function'>⊐</span> <span class='String'>&quot;one&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;eight&quot;</span><span class='Ligature'>‿</span><span class='String'>&quot;two&quot;</span>
+⟨ 1 4 2 ⟩
+</pre>
+<p><code><span class='Value'>𝕩</span><span class='Function'>∊</span><span class='Value'>𝕨</span></code> is the same as <code><span class='Paren'>(</span><span class='Value'>𝕨</span><span class='Function'>⊐</span><span class='Value'>𝕩</span><span class='Paren'>)</span><span class='Function'>&lt;≠</span><span class='Value'>𝕨</span></code>. Note the reversal of arguments! In both <code><span class='Function'>∊</span></code> and <code><span class='Function'>⊐</span></code>, the open side points to the searched-in argument and the closed side points to the searched-for argument. Relatedly, in Select (<code><span class='Function'>⊏</span></code>), the open side points to the selected argument, which is more like the searched-in argument in that its cells are generally accessed out of order (the searched-for argument is most like the selection result <code><span class='Value'>𝕨</span><span class='Function'>⊏</span><span class='Value'>𝕩</span></code>).</p>
+<p>Index of always returns exactly one number, even if there are multiple matches, or no matches at all. To find the indices of all matches, start with <a href="match.html">Match</a> <a href="map.html">Each</a>, then <a href="replicate.html#indices">Indices</a> (I didn't mean for it to sound so repetitive! It just happened!).</p>
+<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=LyAibGV0dGVycyIg4omhwqg8ICdlJyAgICAgICAgIyBNYW55IHRvIG9uZQoKImxldHRlcnMiICg84oiYL8uY4omh4oycy5wpICJsZXQiICAjIE1hbnkgdG8gbWFueQ==">↗️</a><pre> <span class='Function'>/</span> <span class='String'>&quot;letters&quot;</span> <span class='Function'>≡</span><span class='Modifier'>¨</span><span class='Function'>&lt;</span> <span class='String'>'e'</span> <span class='Comment'># Many to one
+</span>⟨ 1 4 ⟩
+
+ <span class='String'>&quot;letters&quot;</span> <span class='Paren'>(</span><span class='Function'>&lt;</span><span class='Modifier2'>∘</span><span class='Function'>/</span><span class='Modifier'>˘</span><span class='Function'>≡</span><span class='Modifier'>⌜˜</span><span class='Paren'>)</span> <span class='String'>&quot;let&quot;</span> <span class='Comment'># Many to many
+</span>⟨ ⟨ 0 ⟩ ⟨ 1 4 ⟩ ⟨ 2 3 ⟩ ⟩
+</pre>
+<h2 id="progressive-index-of">Progressive Index of</h2>
+<p>Progressive Index of (<code><span class='Function'>⊒</span></code>), as the name and glyph suggest, is a more sophisticated variant of Index of. Like Index of, it returns either <code><span class='Function'>≠</span><span class='Value'>𝕨</span></code> or an index of a cell from <code><span class='Value'>𝕨</span></code> that matches the given cell of <code><span class='Value'>𝕩</span></code>. Unlike Index of, no index except <code><span class='Function'>≠</span><span class='Value'>𝕨</span></code> can ever be repeated. Progressive Index of returns the index of the first <em>unused</em> match, provided there's still one left.</p>
+<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=ImFhYSIg4oqSICJhYWFhYSIKCiJhYWFiYiIg4oqSICJhYmFiYWJhYmFiIg==">↗️</a><pre> <span class='String'>&quot;aaa&quot;</span> <span class='Function'>⊒</span> <span class='String'>&quot;aaaaa&quot;</span>
+⟨ 0 1 2 3 3 ⟩
+
+ <span class='String'>&quot;aaabb&quot;</span> <span class='Function'>⊒</span> <span class='String'>&quot;ababababab&quot;</span>
+⟨ 0 3 1 4 2 5 5 5 5 5 ⟩
+</pre>
+<p>Above we said that <code><span class='Value'>𝕩</span><span class='Function'>∊</span><span class='Value'>𝕨</span></code> is <code><span class='Paren'>(</span><span class='Value'>𝕨</span><span class='Function'>⊐</span><span class='Value'>𝕩</span><span class='Paren'>)</span><span class='Function'>&lt;≠</span><span class='Value'>𝕨</span></code>, so that <code><span class='Function'>⊐</span><span class='Modifier'>˜</span><span class='Function'>&lt;≠</span><span class='Modifier2'>∘</span><span class='Function'>⊢</span></code> is an implementation of Member of. The corresponding <code><span class='Function'>⊒</span><span class='Modifier'>˜</span><span class='Function'>&lt;≠</span><span class='Modifier2'>∘</span><span class='Function'>⊢</span></code> implements <em>progressive</em> member of, that is, membership on <a href="https://en.wikipedia.org/wiki/Multiset">multisets</a>. So if <code><span class='Value'>𝕩</span></code> contains two copies of <code><span class='String'>'a'</span></code>, only the first to instances of <code><span class='String'>'a'</span></code> in <code><span class='Value'>𝕨</span></code> are considered to belong to it. And like membership is useful for set intersection and difference, progressive membership gives multiset versions of these.</p>
+<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=ImFhYmJjYyIgKOKKkMucPOKJoOKImOKKoikgImJhYSIKCiJhYWJiY2MiICjiipLLnDziiaDiiJjiiqIpICJiYWEiCgoiYWFiYmNjIiAoKOKKksucPeKJoOKImOKKoikv4oqjKSAiYmFhIiAgIyBNdWx0aXNldCBkaWZmZXJlbmNl">↗️</a><pre> <span class='String'>&quot;aabbcc&quot;</span> <span class='Paren'>(</span><span class='Function'>⊐</span><span class='Modifier'>˜</span><span class='Function'>&lt;≠</span><span class='Modifier2'>∘</span><span class='Function'>⊢</span><span class='Paren'>)</span> <span class='String'>&quot;baa&quot;</span>
+⟨ 1 1 1 1 0 0 ⟩
+
+ <span class='String'>&quot;aabbcc&quot;</span> <span class='Paren'>(</span><span class='Function'>⊒</span><span class='Modifier'>˜</span><span class='Function'>&lt;≠</span><span class='Modifier2'>∘</span><span class='Function'>⊢</span><span class='Paren'>)</span> <span class='String'>&quot;baa&quot;</span>
+⟨ 1 1 1 0 0 0 ⟩
+
+ <span class='String'>&quot;aabbcc&quot;</span> <span class='Paren'>((</span><span class='Function'>⊒</span><span class='Modifier'>˜</span><span class='Function'>=≠</span><span class='Modifier2'>∘</span><span class='Function'>⊢</span><span class='Paren'>)</span><span class='Function'>/⊣</span><span class='Paren'>)</span> <span class='String'>&quot;baa&quot;</span> <span class='Comment'># Multiset difference
+</span>"bcc"
+</pre>
+<p>This primitive gives an interesting way to implement the <a href="order.html#ordinals">ordinals</a> pattern that might be easier to understand than the APL classic <code><span class='Function'>⍋⍋</span></code> (it's probably a little slower though). The idea is to use the sorted array as the left argument to <code><span class='Function'>⊒</span></code>. Now the index returned for each cell is just where it ended up in that sorted order. If we used ordinary Index of then equal cells would share the smallest index; Progressive Index of means ties are broken in favor of earlier cells.</p>
+<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4o2L4oiY4o2LICJhZGViY2VkYmEiCgriiKfiirjiipIgImFkZWJjZWRiYSIKCuKIp+KKuOKKkCAiYWRlYmNlZGJhIiAgIyBUaWVzIGluY2x1ZGVk">↗️</a><pre> <span class='Function'>⍋</span><span class='Modifier2'>∘</span><span class='Function'>⍋</span> <span class='String'>&quot;adebcedba&quot;</span>
+⟨ 0 5 7 2 4 8 6 3 1 ⟩
+
+ <span class='Function'>∧</span><span class='Modifier2'>⊸</span><span class='Function'>⊒</span> <span class='String'>&quot;adebcedba&quot;</span>
+⟨ 0 5 7 2 4 8 6 3 1 ⟩
+
+ <span class='Function'>∧</span><span class='Modifier2'>⊸</span><span class='Function'>⊐</span> <span class='String'>&quot;adebcedba&quot;</span> <span class='Comment'># Ties included
+</span>⟨ 0 5 7 2 4 7 5 2 0 ⟩
+</pre>
+<p>Here's a goofy code golf tip: if the two arguments to Progressive Index of are the same, then every cell will be matched to itself, because all the previous indices are taken but the current one does match. So <code><span class='Function'>⊒</span><span class='Modifier'>˜</span></code> is the same as <code><span class='Function'>↕</span><span class='Modifier2'>∘</span><span class='Function'>≠</span></code>.</p>
+<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oqSy5wgImFueXRoaW5nIGF0IGFsbCI=">↗️</a><pre> <span class='Function'>⊒</span><span class='Modifier'>˜</span> <span class='String'>&quot;anything at all&quot;</span>
+⟨ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ⟩
+</pre>