rslt ← {new} (mod ##.at sel) old        ⍝ item substitution: new at sel in old
rslt ←       (new ##.at sel) old

A functional alternative to indexed and modified-indexed assignment.

[new] items are [mod]ified with the [sel]ected items of [old] to produce [rslt].

    10(× at 2 4)⍳5              ⍝ 10-fold at 2nd and 4th items in 1..5
1 20 3 40 5

    ⌽ at(2+⍳4) ⊢'redrawing'     ⍝ reversal of section of character vector
rewarding

If the left operand is an _array_, the array value is used for the substitution:

    'er'at 2 3 ⊢'Pinky'         ⍝ substitution of 2nd and 3rd item
Perky

Notice that a _nested_ [sel]ector implies "choose" or "reach" indexing:

    '·'at(2 4∘.,2 4) ⊢5 5⍴⎕A            ⍝ choose indexing
ABCDE
F·H·J
KLMNO
P·R·T
UVWXY

    '⌽⍉'at(1 5)(2 2) ⊢'hello' 'world'   ⍝ reach indexing
┌─────┬─────┐
│hell⌽│w⍉rld│
└─────┴─────┘

[at] is sensitive to index origin (⎕IO). See the examples with ⎕io←0 below.

Finally, if the [sel]ector is a _function_:  when  applied to the [old] value it
must return a boolean array of the same shape, which is used for the selection:

    '*'at(<∘0) 3 ¯1 4 1 ¯5      ⍝ function selector: stars for -ive values
3 * 4 1 *

    ⊢text ← 3 4⍴'twaseverthus'  ⍝ subject text matrix
twas
ever
thus

    vowels_in ← ∊∘'AaEeIiOoUu'  ⍝ boolean selection function

    ucase at vowels_in text     ⍝ upper case vowels                 →lcase←
twAs
EvEr
thUs

Tracing the boolean function shows its intermediate result:

    ucase at (vowels_in tc) text    ⍝ trace of selection            →tc←
 ∊∘AaEeIiOoUu  twas  =>  0 0 1 0
               ever      1 0 1 0
               thus      0 0 1 0
twAs
EvEr
thUs

As the next trace shows, with a boolean-returning function [sel]ector, [at] pre-
sents a _vector_ of the selected items to its  [mod]ifier function.  [mod]  must
return a vector of the same length, or single-item, result:

    (ucase tc)at vowels_in text     ⍝ trace of modification
 ∇  aeeu  =>  AEEU
twAs
EvEr
thUs

Summary:

    ┌──────────────────────────┬───────────────────────────────────┐
    │  (A@B)C ←→ C[B]←A        │   A@g C ←→ C[⍸g C]←A              │
    ├──────────────────────────┼───────────────────────────────────┤
    │ ⍶(f@B)C ←→ C[B]←⍶ f C[B] │ ⍶ f@g C ←→ C[I]←⍶ f C[I] ⊣ I←⍸g C │
    └──────────────────────────┴───────────────────────────────────┘

     where:
        @  at
       ←→  "is analogous to"
    A B C  arrays
      f g  functions
        ⍸  {(,⍵)/,⍳⍴⍵}  "where"
        ⍶  optional left argument (may or may not be present)

(muse:

    Aaron Hsu suggests a conforming extension: a boolean function right  operand
    could be allowed to return a result of lower rank than the  right  argument.
    The selection would be applied to the (⍴⍴⍵⍵ ⍵) frame of ⍵. The change to the
    model would be:

    < 3=⎕NC'⍵⍵':⍺ ⍺⍺ ∇∇((,⍵⊢¨~~⍵⍵ ⍵)/,⍳⍴⍵)⊢⍵  ⍝ selector is function: bool mask
                        ¯¯¯¯¯¯¯¯¯¯¯¯
    > 3=⎕NC'⍵⍵':⍺ ⍺⍺ ∇∇((,⍵{⍵⊣¨⍤(-≢⍴⍵)⊢⍺}~~⍵⍵ ⍵)/,⍳⍴⍵)⊢⍵  ⍝ selector is function
                        ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    then:

        '*'at{1 0 1} 3 4⍴⍳12            ⍝ vector mask vs matrix right argument
    * * * *
    4 5 6 7
    * * * *

        10×at{3 4⍴1 0 0} 3 4 5⍴⍳5       ⍝ rank 2 mask vs rank 3 right argument
    0 10 20 30 40
    0  1  2  3  4
    0  1  2  3  4
    0 10 20 30 40

    0  1  2  3  4
    0  1  2  3  4
    0 10 20 30 40
    0  1  2  3  4

    0  1  2  3  4
    0 10 20 30 40
    0  1  2  3  4
    0  1  2  3  4

    Jay Foad points out that some decisions remain regarding 3-way conformabil-
    ity among
    - left argument (or operand) array;
    - boolean array result;
    - right argument array:

        'ABCDEF'at{1 0 1} 3 3⍴⍳9
    A B C
    4 5 6
    D E F

    or would we require the left operand to conform with the selection?:

        (2 3⍴'ABCDEF')at{1 0 1} 3 3⍴⍳9
    A B C
    4 5 6
    D E F

    would items replicate along selected cells?:

        'AB'at{1 0 1}3 3⍴⍳9
    A A A
    4 5 6
    B B B
)

Background
----------
Classic APL lacks a general, non-procedural  way to express the "triadic" funct-
ion: "THESE items at THOSE positions in THIS array".  For example: "Asterisks at
the vowel positions in the alphabet". Rather,  it  compels  us  to use a 3-stage
_procedure_ and requires us to name an intermediate subject array:

    [1] Name the subject array,                 ⍝ THIS ← ...
    [2] Mutate the array, using its name,       ⍝ THIS[THOSE]←THESE
    [3] Dereference the name for the result.    ⍝ ... THIS

(muse:

    Ideally, the language should not force us into naming the results  of  expr-
    essions merely for reasons of syntax. Better that we choose to  name  things
    solely in order to create mental abstractions that are easier to comprehend.

    Sound bite: Names enable us to chop up our programs into mind-sized chunks.

    As an example, the following three code fragments are  equivalent  and  each
    reducible by the language evaluator to the syntax tree on the right.  Ideal-
    ly, the choice should depend only on the perceived simplicity of expression.

    (+⌿⍵)÷≢⍵                                    ⍝
                                                ⍝        ┌───┼─┐
    sum←+⌿⍵ ⋄ num←≢⍵  ⋄ sum÷num                 ⍝        ├─┐ ÷ ├─┐
                                                ⍝      ┌─┤ ⍵   ≢ ⍵
    sum←+⌿ ⋄ num←≢ ⋄ (sum ⍵)÷num ⍵              ⍝      + ⌿

    This approach differs from that of "variable-centric" languages, where names
    must be  declared  to reserve storage locations and otherwise participate in
    the mechanics of evaluation.
)

Technical notes:

1. Internally [at] uses regular indexed assignment:

        A⊣A[I]←⍺ ⍺⍺(A←⍵)[I]             ⍝ nb: duplicate indices ignored

   in preference to the shorter _modified_ indexed assignment:

        A⊣A[I]←⍺⍺⍨←⍺⊣A←⍵                ⍝ (not used)

   in order for the _whole_ of the selected sub-array, rather than just one item
   at a time, to be presented to  the  [mod]ification function ⍺⍺.  This allows,
   for example, operations on whole rows:

        1 ⌽at 2 ⊢3 5⍴⍳15                ⍝ 1-rotation of middle row.
     1  2  3  4  5
     7  8  9 10  6
    11 12 13 14 15

2. This simple model claims only to be adequate for experimentation. An industr-
   ial strength version would need to add:

 - Better error messages: in particular indication of  whether a problem has oc-
   curred with the left or right operand.

 - Defence  against  the possibility of a nameclash between at's local names and
   global references from within its operand functions.

Examples:

    '*'at 2 4 ⊢5↑⎕A             ⍝ '*' at 2nd and 4th items
A*C*E

    M ← 5 5⍴ ⍳25                ⍝ 5×5 numeric matrix

    0 at 2 4 ⊢M                 ⍝ cf: M[2 4;]←0
 1  2  3  4  5
 0  0  0  0  0
11 12 13 14 15
 0  0  0  0  0
21 22 23 24 25

    (2 5⍴⎕A) at 2 4 ⊢M          ⍝ cf: M[2 4;]←2 5⍴⎕A
 1  2  3  4  5
 A  B  C  D  E
11 12 13 14 15
 F  G  H  I  J
21 22 23 24 25

    0 at 2 4⍤1 ⊢M               ⍝ cf: M[;2 4]←0
 1 0  3 0  5
 6 0  8 0 10
11 0 13 0 15
16 0 18 0 20
21 0 23 0 25

    (⍉2 5⍴⎕A)⊣at 2 4⍤1 ⊢M       ⍝ cf: M[;2 4]←⍉2 5⍴⎕A
 1 A  3 F  5
 6 B  8 G 10
11 C 13 H 15
16 D 18 I 20
21 E 23 J 25

    0 at 2 3 4⍤1 at 2 3 4 ⊢M    ⍝ cf: M[2 3 4;2 3 4]←0      nested [at]s
 1  2  3  4  5
 6  0  0  0 10
11  0  0  0 15
16  0  0  0 20
21 22 23 24 25

    (2 2⍴⎕A)at(1 5∘.,1 5) ⊢M    ⍝ cf: M[1 5;1 5]←2 2⍴⎕A     choose indexing
 A  2  3  4  B
 6  7  8  9 10
11 12 13 14 15
16 17 18 19 20
 C 22 23 24  D

    ⌽ at 2 4 ⊢M                 ⍝ reverse of 2nd and 4th rows (boustrophedon)
 1  2  3  4  5
10  9  8  7  6
11 12 13 14 15
20 19 18 17 16
21 22 23 24 25

    ⊖ at 2 4 ⊢M                 ⍝ exchange of 2nd and 4th rows (Gaussian elim.)
 1  2  3  4  5
16 17 18 19 20
11 12 13 14 15
 6  7  8  9 10
21 22 23 24 25

    ⍙←{⍵⍵⍣¯1 ⍺⍺ ⍵⍵ ⍵}           ⍝ model for under/dual: ⍢

    ⌽ at 2 4⍙⍉ ⊢M               ⍝ reverse of alternate columns (Anc. Mongolian)
 1 22  3 24  5
 6 17  8 19 10
11 12 13 14 15
16  7 18  9 20
21  2 23  4 25

    10×at 2 4 ⍳5                ⍝ 10× at ...
1 20 3 40 5

    10×at 2 2 ⍳5                ⍝ nb: dups ignored
1 20 3 4 5

⍝ Notice how (A at ...) differs from (A⊣at ...) if the derived function is
⍝ applied by an operator such as each (¨) or rank (⍤):

    '<>' at(1+⍳2)¨ 'lft' 'rgt'  ⍝ left _operand_ bound to [at]
┌───┬───┐
│l<>│r<>│
└───┴───┘
    '<>'⊣at(1+⍳2)¨ 'lft' 'rgt'  ⍝ left _argument_ distributed by each
┌───┬───┐
│l<<│r>>│
└───┴───┘

⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ boolean selection:

    '*'at(∊∘'AEIOU') ⎕A         ⍝ vowels
*BCD*FGH*JKLMN*PQRST*VWXYZ

    100×at(2∘|)M                ⍝ alternately × 100
 100    2  300    4  500
   6  700    8  900   10
1100   12 1300   14 1500
  16 1700   18 1900   20
2100   22 2300   24 2500

    {∊¨(0=3 5∘|¨⍵)/¨⊂'fizz' 'buzz'}at{0∨.=3 5∘.|⍵} 2 3 8⍴⍳48    ⍝ FizzBuzz
┌────┬────┬────┬────┬────────┬────────┬────────┬────┐
│1   │2   │fizz│4   │buzz    │fizz    │7       │8   │
├────┼────┼────┼────┼────────┼────────┼────────┼────┤
│fizz│buzz│11  │fizz│13      │14      │fizzbuzz│16  │
├────┼────┼────┼────┼────────┼────────┼────────┼────┤
│17  │fizz│19  │buzz│fizz    │22      │23      │fizz│
└────┴────┴────┴────┴────────┴────────┴────────┴────┘
┌────┬────┬────┬────┬────────┬────────┬────────┬────┐
│buzz│26  │fizz│28  │29      │fizzbuzz│31      │32  │
├────┼────┼────┼────┼────────┼────────┼────────┼────┤
│fizz│34  │buzz│fizz│37      │38      │fizz    │buzz│
├────┼────┼────┼────┼────────┼────────┼────────┼────┤
│41  │fizz│43  │44  │fizzbuzz│46      │47      │fizz│
└────┴────┴────┴────┴────────┴────────┴────────┴────┘

⍝ Roger Hui's model of "Mesh" and "Mask", implemented using [at]:

⍝ Mesh (APL, 1962) -------------------------------------------------------------

⍝   c ← a (u Mesh) b    ←→    ((~u)⌿c)≡a and (u⌿c)≡b

    ⎕io ← 0
    a ← 33 44 55
    b ← ⍳10
    u ← 1 1 0 1 1 1 1 0 1 1 1 0 1

    ↑ u (b at(u⊣⊣) (~u)⍀a)
1 1  0 1 1 1 1  0 1 1 1  0 1
0 1 33 2 3 4 5 44 6 7 8 55 9

    c ← b at(u⊣⊣) (~u)⍀a
    a ≡ (~u)⌿c
1
    b ≡ u⌿c
1
    c ≡ b at(u/⍳≢u) ⊢(~u)⍀a
1

⍝ The last version with the integer right operand is required for meshing major
⍝ cells.

      a ← 2⍴⍤0 ⊢33 44 55
      b ← 2⍴⍤0⍳10
      c ← b at(u/⍳≢u) ⊢(~u)⍀a
      a ≡ (~u)⌿c
1
      b ≡ u⌿c
1

⍝ Mask (APL, 1962) -------------------------------------------------------------

⍝   c ← a (u Mask) b    ←→    ((~u)⌿c)≡(~u)⌿a and (u⌿c)≡u⌿b

For numeric vectors a and b,

    a ← ⍳13
    b ← 20+⍳13
    u ← 13⍴0 0 1
    c ← (a×~u) + b×u

    ↑ u (b (u⌿⊣)at(u⊣⊣) a)
0 0  1 0 0  1 0 0  1 0  0  1  0
0 1 22 3 4 25 6 7 28 9 10 31 12

    c ← b (u⌿⊣)at(u⊣⊣) a
    ((~u)⌿c) ≡ (~u)⌿a
1
    (u⌿c)≡u⌿b
1
    c ≡ b (u⌿⊣)at(u/⍳≢u) ⊢a
1

⍝ Roger says: again, the last version with the integer right operand is
⍝ required for masking major cells.

    a ← 2⍴⍤0 ⊢⍳13
    b ← 2⍴⍤0 ⊢20+⍳13
    c ← b (u⌿⊣)at(u/⍳≢u) ⊢a
    ((~u)⌿c) ≡ (~u)⌿a
1
    (u⌿c)≡u⌿b
1

⍝ Here is how the inner "Gaussian elimination" function from →gauss_jordan←
⍝ might look when converted from a procedural style, which mutates the matrix
⍝ in two places:

    elim←{                                  ⍝ elimination of row/col ⍺.
        mat←⍵                               ⍝ name matrix for updating.
        p←⍺+{⍵⍳⌈/⍵}|⍺↓mat[;⍺]               ⍝ index of pivot row.
        mat[⍺ p;]←mat[p ⍺;]                 ⍝ exchange ⍺th and pth rows.    !!
        mat[⍺;]÷←mat[⍺;⍺]                   ⍝ reduce col diagonal to 1.     !!
        mat-(mat[;⍺]×⍺≠⍳≢⍵)∘.×mat[⍺;]       ⍝ reduce col off-diagonals to 0.
    }

⍝ ... to a denotative definition using [at], which does not.  To better show the
⍝ impact of a _primitive_ operator, the glyph "@" is chosen to represent [at] in
⍝ these coding examples:

    elim←{                                  ⍝ elimination of row/col ⍺.
        p←⍺+{⍵⍳⌈/⍵}|⍺↓⍵[;⍺]                 ⍝ index of pivot row.
        swap←⊖@⍺ p⊢⍵                        ⍝ ⍺th and pth rows exchanged.   !
        mat←swap[⍺;⍺]÷⍨@⍺⊢swap              ⍝ col diagonal reduced to 1.    !
        mat-(mat[;⍺]×⍺≠⍳≢⍵)∘.×mat[⍺;]       ⍝ col off-diagonals reduced to 0.
    }

⍝ and here's the equivalent indexed-assignment replacement for function →span←

    span←{                      ⍝ Spanning tree for graph ⍺ from vertex(ices) ⍵.
        graph←⍺                 ⍝ ⍺ is graph vector.
        tree←¯2+(⍳⍴⍺)∊⍵         ⍝ initial spanning tree.
        free←{(¯2=tree[⍵])/⍵}   ⍝ free vertices in ⍵.
        {                       ⍝ visit adjacent vertices:
            ⍵≡⍬:tree            ⍝ no vertices: done.
            next←free¨graph[⍵]  ⍝ next vertices to visit.
            back←↑,/⍵+0×next    ⍝ back links.
            wave←↑,/next        ⍝ wave front.
            tree[wave]←back     ⍝ set back links in tree        !!
            ∇ ∪wave             ⍝ advance wave front.
        }⍵                      ⍝ from starting vertex.
    }

⍝ which becomes:

    span←{                        ⍝ Spanning tree for graph ⍺ from vertex(ices).
        graph←⍺                   ⍝ ⍺ is graph vector.
        tree←¯2+(⍳⍴⍺)∊⍵           ⍝ initial spanning tree.
        free←{(¯2=⍺[⍵])/⍵}        ⍝ free vertices in ⍵.                 !
        tree{                     ⍝ ⍺: partial spanning tree.           !
            ⍵≡⍬:⍺                 ⍝ no vertices: done.                  !
            next←⍺∘free¨graph[⍵]  ⍝ next vertices to visit.             !
            back←↑,/⍵+0×next      ⍝ back links.
            wave←↑,/next          ⍝ wave front.
            part←back@wave⊢⍺      ⍝ back links at wave front            !
            part ∇ ∪wave          ⍝ partial tree; advanced wave front   !
        }⍵                        ⍝ ⍵: next wave of vertices to visit.
    }

⍝ Finally, here are some more code snippets from this workspace, which might be
⍝ improved with the introduction of a primitive @ (say) operator. The lines are
⍝ marked: <old and >new:

<dots[11-14] dmask←,2=flood-xdents ⋄ crep←⍵ ⋄ crep[dmask/,⍳⍴⍵]←⍺ ⋄ crep
>dots[11]    ⍺@{2=flood-xdents}⍵

<packZ[14]  tree←⍵ ⋄ tree[posn]←⊂sub
>packZ[14]  tree←(⊂sub)@posn⊢⍵

<remlink[1-2] graph←⍺ ⋄ fm to←⍵ ⋄ graph⊣(fm⊃graph)~←to
>remlink[1-2]           fm to←⍵ ⋄ ~∘to¨@fm⊢⍺

<ssword[5-7] cvex←⍺ words srce ⋄ mask←cvex∊⊂find ⋄ ∊cvex⊣(mask/cvex)←⊂repl
>ssword[7]   ∊(⊂repl)@{⍵∊⊂find}⍺ words srce

<sudoku[30] ⍵∘{m←⍺ ⋄ (i⊃m)←,⍵ ⋄ m}¨i⊃⍵
>sudoku[30] (,¨¨i⊃⍵)⊣@i¨⊂⍵

<notes.sudoku[242 244] pvec←{(⍺ avl ⍵)(⍺ at)¨⊂⍵} ⋄ at←{⍵+⍺×(⍳⍴⍵)∊⊂⍺⍺}
>notes.sudoku[242]     pvec←{(⍺ avl ⍵)⊣@(⊂⍺)¨⊂⍵}

<von[27] (b/tx)←ucase b/tx ⋄ ¯1↓tx
>von[27] ¯1↓ucase@{b}tx

<xtabs[19-21] text←⍵ ⋄ (tabs/text)←⎕UCS 9 ⋄ (pretab≤tabs)/text
>xtabs[19]    (pretab≤tabs)/(⎕UCS 9)@{tabs}⍵

<wsdiff[38-39] mask←,(⍵=' ')∧(⍴⍵)⍴cols⍴4↑1 ⋄ mat←⍵ ⋄ mat⊣(mask/,mat)←'·'
>wsdiff[38]     '·'@{(⍵=' ')∧(⍴⍵)⍴cols⍴4↑1}⍵

See also: sam select ucase tc gauss_jordan

Back to: contents

Back to: Workspaces