R∆ ← L (index ##.merge '∘' ) R ⍝ Suggestion for a merge operator.
R∆ ← L (index ##.merge axes) R ⍝ right operand specifies axes,
R∆ ← L (selfn ##.merge '∘' ) R ⍝ left operand can be selection fn.
[merge] has two cases:
With an array left operand:
¯¯¯¯¯
⍺(⍺⍺ merge'∘' )⍵ → (⍺⍺⌷⍵)←⍺ ⋄ ⍵
⍺(⍺⍺ merge axis)⍵ → (⍺⍺⌷[axis]⍵)←⍺ ⋄ ⍵
With a function left operand:
¯¯¯¯¯¯¯¯
⍺(⍺⍺ merge'∘')⍵ → ((,⍺ ⍺⍺ ⍵)/,⍵)←⍺ ⋄ ⍵
Note that, from Dyalog V13.0, index function ⌷ can take a short left argument
vector. This feature is passed along to [merge].
Classic APL lacks a general, non-procedural way to express the "triadic" funct-
ion: "THIS array but with THOSE items at THESE positions". For example: the let-
ters of the alphabet but with stars instead of vowels.
Rather, it encourages us to use a 3-stage procedure and to supply a name for the
subject array:
[1] Name the subject array, ⍝ THIS ← ⍵
[2] Update the array, using its name, ⍝ THIS[THESE]←THOSE
[3] Dereference the name for the result. ⍝ THIS
(muse:
Ideally, the language should never force us into naming the results of expr-
essions. Better that we choose to name things only in order to create mental
abstractions that are easier for us to work with. For example, the following
three code fragments are equivalent and each reducible by the language eval-
uator to the syntax tree on the right:
(+/⍵)÷⍴⍵ ⍝
⍝ ┌───┼─┐
sum←+/⍵ ⋄ num←⍴⍵ ⋄ sum÷num ⍝ ├─┐ ÷ ├─┐
⍝ ┌─┤ ⍵ ⍴ ⍵
sum←+/ ⋄ num←⍴ ⋄ (sum ⍵)÷num ⍵ ⍝ + /
Sound bite: "Names allow us to chop up our programs into mind-sized pieces."
)
(muse:
If [merge] were to be implemented as a primitive operator, the "axis case"
would use APL's regular axis operator and [merge] itself would be monadic.
For example, suppose we chose "→" as the symbol for merge, then we might see
expressions such as these:
⍺(v→)⍵ ⍝ with all axes selected: (⍴v)≡⍴⍴⍵.
⍺(v→[1 0])⍵ ⍝ with explicit axes: 2=⍴v.
⍺ {...⍵}→ ⍵ ⍝ with boolean-returning selection function.
)
Larg vs Rarg
------------
With indexed assignment, the "old" array is on the left and the "new" values are
on the right:
OLD[⍳3]←'NEW'
We save on parentheses if we arrange for the value that would, more often than
not be named or literal, to occur on the left. Here are examples of both cases:
MAT (... merge) lcase MAT ⍝ Lower-casification of selected chars in MAT.
⍝ old (sel merge) new ⍝ say: "Old with sel[ected items] set to new".
vs: ¯¯¯¯
'*' (... merge) 5 5⍴⎕A ⍝ Stars for selected chars in matrix.
⍝ new (sel merge) old ⍝ say: "New for sel[ection] in old".
¯¯¯¯
Arguably more important than saving parentheses, is the impact of argument order
on the notation as tool-of-thought. It seems (to JMS) to be less distracting if
the modification (new value) is on the left; merges are applied as refinements
to the result of the expression developed on the right. Further:
- Functional programming uses a notation: [new/old]expr "new for old in expr",
where "new" and "old" are strings. "old" specifies positions of matches within
"expr". [merge]'s left operand is simply a generalisation: [new/expr∊old]expr.
- Looking at the vocalisation (say:) above, the first seems more procedural and
the second more declarative. Declarative is, in general, easier on the mind.
- It is more convenient if the ambi-valent selection function takes the old
array as right argument (and so less confusing if this is also the right argu-
ment of the derived function as a whole). See examples below.
NB: A selection _function_ as operand may specify a non-rectanglar subset of the
items of the subject array. In this case, the items of a non-single right argu-
ment vector populate the selection in ravel order.
A[(,⍺⍺ ⍵)/,⍳⍴A←⍵]←⍺
As in the above code, LENGTH ERROR will be generated unless ⍺ is a single-item
array or is vector with the same number of items as the selection.
(muse:
To select a single item in a rank-⍵ array, we need an ⍵-vector. Dyalog im-
plements three forms of indexing: "simple", "choose" and "reach".
Simple indexing (v⍴...)[x;y;...] uses a "v-tuple" of arrays, whose cartesian
product provides the array of v-vectors that selects a rectangular slice of
the subject array. The shape of the result is the shape of the cartesian
product.
Choose indexing (v⍴...)[x y ...] uses an array of v-vectors, each item of
which selects directly from the subject array. The shape of the result is
the shape of the selection array.
Simple and choose indexing converge in the case where the subject array is a
vector.
Reach indexing (u⍴⊂v⍴⊂...)[x y ...] uses an array of vector "paths", where
each path is a vector of (u v ..)-vectors, each item of which selects into
items of items of the subject array. The shape of the result is the shape of
the selection array.
[merge] addresses the simple and choose cases with its array and function
operands, respectively. It does not help directly with the equivalent reach
indexing.
)
Examples:
vec←11 to 19 ⍝ vector of numbers.
vec
11 12 13 14 15 16 17 18 19
0 (2∘| merge'∘') vec ⍝ function operand: with zeros for odd nums.
0 12 0 14 0 16 0 18 0
'x' ({1=?9⍴2} merge'∘') vec ⍝ random merge of 'x'
x 12 x 14 xx 17 18 19
0 (4 merge'∘') vec ⍝ 4th item replaced with 0.
11 12 13 0 15 16 17 18 19
0 ((⊂3 4 5) merge'∘') vec ⍝ 3rd-5th items replaced with 0s.
11 12 0 0 0 16 17 18 19
1 2 3 ((⊂4 3 2) merge'∘') vec ⍝ 4th-2nd items replaced with 1-3.
11 3 2 1 15 16 17 18 19
mat←10⊥¨⍳4 5 ⍝ matrix of numbers.
mat
11 12 13 14 15
21 22 23 24 25
31 32 33 34 35
41 42 43 44 45
0({0=3|⍵}merge'∘')mat ⍝ function operand: every 3rd item replaced.
11 0 13 14 0
0 22 23 0 25
31 32 0 34 35
41 0 43 44 0
3 ({0=⍺|⍵}merge'∘')mat ⍝ dyadic operand: every ⍺th item replaced.
11 3 13 14 3
3 22 23 3 25
31 32 3 34 35
41 3 43 44 3
0 (2 merge 1) mat ⍝ 2nd row replaced with 0s.
11 12 13 14 15
0 0 0 0 0
31 32 33 34 35
41 42 43 44 45
0 (4 merge 2) mat ⍝ 4th col replaced with 0s.
11 12 13 0 15
21 22 23 0 25
31 32 33 0 35
41 42 43 0 45
0 ((⊂2 3)merge 1) mat ⍝ 2nd & 3rd rows replaced with 0s.
11 12 13 14 15
0 0 0 0 0
0 0 0 0 0
41 42 43 44 45
(2 5⍴⍳10) ((⊂3 2)merge 1) mat ⍝ 3rd & 2nd rows replaced with numbers.
11 12 13 14 15
6 7 8 9 10
1 2 3 4 5
41 42 43 44 45
0 ((2 3)(2 4)merge'∘') mat ⍝ 2nd 3rd rows, 2nd 4th cols ← 0.
11 12 13 14 15
21 0 23 0 25
31 0 33 0 35
41 42 43 44 45
(2 2⍴⍳4) ((2 3)(2 4)merge'∘') mat ⍝ .. .. numbers.
11 12 13 14 15
21 1 23 2 25
31 3 33 4 35
41 42 43 44 45
cube←10⊥¨⍳2 3 4 ⍝ cuboid of numbers.
cube
111 112 113 114
121 122 123 124
131 132 133 134
211 212 213 214
221 222 223 224
231 232 233 234
0 (2 merge 1) cube ⍝ 2nd plane replaced with 0s.
111 112 113 114
121 122 123 124
131 132 133 134
0 0 0 0
0 0 0 0
0 0 0 0
0 (2 merge 2) cube ⍝ 2nd rows replaced with 0.
111 112 113 114
0 0 0 0
131 132 133 134
211 212 213 214
0 0 0 0
231 232 233 234
0 (2 merge 3) cube ⍝ 2nd cols replaced with 0s.
111 0 113 114
121 0 123 124
131 0 133 134
211 0 213 214
221 0 223 224
231 0 233 234
(3 4⍴⍳12) (2 merge 1) cube ⍝ 2nd plane replaced with matrix.
111 112 113 114
121 122 123 124
131 132 133 134
1 2 3 4
5 6 7 8
9 10 11 12
(⍳3) (2 1 merge 1 3) cube ⍝ 2nd row, 1st col replaced with vector.
111 112 113 114
121 122 123 124
131 132 133 134
1 212 213 214
2 222 223 224
3 232 233 234
99 (⍬ merge '∘') 88 ⍝ scalar case.
99
⍝ Here's a handy derived function:
atfirst ← ⎕io merge ⎕io ⍝ merge with first row (plane, ...).
'⍟' atfirst 4⍴⎕a ⍝ first item of vector.
⍟BCD
'this' atfirst 3 4⍴⎕a ⍝ first row of matrix.
this
EFGH
IJKL
'⎕' atfirst 2 3 4⍴⎕a ⍝ first plane of cube.
⎕⎕⎕⎕
⎕⎕⎕⎕
⎕⎕⎕⎕
MNOP
QRST
UVWX
See also: squad select
Back to: contents
Back to: Workspaces
Trouble seeing APL font?