APL Wiki logo: Difference between revisions

From APL Wiki
Jump to navigation Jump to search
m (Text replacement - "<source" to "<syntaxhighlight")
 
(5 intermediate revisions by the same user not shown)
Line 2: Line 2:


The APL Wiki logo consists of the following [[numeric]] [[matrix]], where each number indicates a circle radius:
The APL Wiki logo consists of the following [[numeric]] [[matrix]], where each number indicates a circle radius:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕IO←0
       ⎕IO←0
       ⌊∘.+⍨.5×4!⍨⍳5
       ⌊∘.+⍨.5×4!⍨⍳5
Line 10: Line 10:
2 4 5 4 2
2 4 5 4 2
1 2 3 2 1
1 2 3 2 1
</source>
</syntaxhighlight>
This page explains, step-by-step, how we generate our SVG logo, using the above expression<ref>[https://codegolf.stackexchange.com/users/78410/bubbler "Bubbler"], message [https://chat.stackexchange.com/transcript/message/52389201#52389201 "52389201"] in ''The Nineteenth Byte'' chat room. Stack Exchange network, 2019-10-31 23:57</ref>. This demonstrates quite a few APL features.
This page explains, step-by-step, how we generate our SVG logo, using the above expression<ref>[https://codegolf.stackexchange.com/users/78410/bubbler "Bubbler"], message [https://chat.stackexchange.com/transcript/message/52389201#52389201 "52389201"] in ''The Nineteenth Byte'' chat room. Stack Exchange network, 2019-10-31 23:57</ref>. This demonstrates quite a few APL features.


Line 17: Line 17:
== Counting ==
== Counting ==
=== From 1 or from 0? ===
=== From 1 or from 0? ===
[[File:Computer console.jpg|thumb|A computer console: <source lang=apl inline>⎕</source>]]
[[File:Computer console.jpg|thumb|A computer console: <syntaxhighlight lang=apl inline>⎕</syntaxhighlight>]]


Whether to count from 0 or from 1 is an old disagreement among programmers. Many APLs let you choose whichever convention you want, but they tend to use 1 by default. To switch convention, we set the variable <source lang=apl inline>⎕IO</source>:
Whether to count from 0 or from 1 is an old disagreement among programmers. Many APLs let you choose whichever convention you want, but they tend to use 1 by default. To switch convention, we set the variable <syntaxhighlight lang=apl inline>⎕IO</syntaxhighlight>:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕IO←0
       ⎕IO←0
</source>
</syntaxhighlight>
By the way, IO stands for [[Index origin|Index Origin]].
By the way, IO stands for [[Index origin|Index Origin]].


We can already now observe a couple of APL's characteristics…
We can already now observe a couple of APL's characteristics…
=== No reserved words ===
=== No reserved words ===
The name <source lang=apl inline>⎕IO</source> begins with the special ''Quad character'' (a stylised console) which symbolises the computer system itself. APL has no reserved words. Rather, all built-in constants, variables, functions and operators have the prefix <source lang=apl inline>⎕</source> indicating that they are part of the system. Because of this, we call them [[quad name]]s.
The name <syntaxhighlight lang=apl inline>⎕IO</syntaxhighlight> begins with the special ''Quad character'' (a stylised console) which symbolises the computer system itself. APL has no reserved words. Rather, all built-in constants, variables, functions and operators have the prefix <syntaxhighlight lang=apl inline>⎕</syntaxhighlight> indicating that they are part of the system. Because of this, we call them [[quad name]]s.


=== Assignments ===
=== Assignments ===
[[Assignment]] is not done with <source lang=apl inline>=</source> like in many other programming languages, but rather with <source lang=apl inline>←</source> which also indicates the direction of the assignment: Whatever is on the right gets put into the name on the left.
[[Assignment]] is not done with <syntaxhighlight lang=apl inline>=</syntaxhighlight> like in many other programming languages, but rather with <syntaxhighlight lang=apl inline>←</syntaxhighlight> which also indicates the direction of the assignment: Whatever is on the right gets put into the name on the left.


=== Generating indices ===
=== Generating indices ===
The <source lang=apl inline>⍳</source> function takes a number ''N'' and [[index generator|generates indices]] until is has made ''N'' [[Index|indices]]. Since we set <source lang=apl inline>⎕IO</source> to 0, we count from 0 until right before ''N'':
The <syntaxhighlight lang=apl inline>⍳</syntaxhighlight> function takes a number ''N'' and [[index generator|generates indices]] until is has made ''N'' [[Index|indices]]. Since we set <syntaxhighlight lang=apl inline>⎕IO</syntaxhighlight> to 0, we count from 0 until right before ''N'':


<source lang=apl>
<syntaxhighlight lang=apl>
       ⍳5
       ⍳5
0 1 2 3 4
0 1 2 3 4
</source>
</syntaxhighlight>


== How many subsets? ==
== How many subsets? ==


Consider a bag with four distinct items. If you stick your hand into the bag and pick two items out, how many different possibilities are there  for which pair you get out? <math>\{(0,1), (0,2), (0,3), (1,2), (1,3), (2,3)\}</math>. APL can tell you this with the [[Binomial]] (<source lang=apl inline>!</source>) function:
Consider a bag with four distinct items. If you stick your hand into the bag and pick two items out, how many different possibilities are there  for which pair you get out? <math>\{(0,1), (0,2), (0,3), (1,2), (1,3), (2,3)\}</math>. APL can tell you this with the [[Binomial]] (<syntaxhighlight lang=apl inline>!</syntaxhighlight>) function:
<source lang=apl>
<syntaxhighlight lang=apl>
       2!4
       2!4
6
6
</source>
</syntaxhighlight>


Notice how APL uses traditional mathematical symbols in a generalised way. The traditional post-fix (after its argument) symbol <math>!</math> is used with a syntax similar to how you'd normally use <math>+</math> or <math>\times</math>. In fact, all APL functions can be used [[Dyad|infix]], like <math>a-b</math> or [[Monad|prefix]], like <math>-b</math>.
Notice how APL uses traditional mathematical symbols in a generalised way. The traditional post-fix (after its argument) symbol <math>!</math> is used with a syntax similar to how you'd normally use <math>+</math> or <math>\times</math>. In fact, all APL functions can be used [[Dyad|infix]], like <math>a-b</math> or [[Monad|prefix]], like <math>-b</math>.


Anyway, how many sets of four could you pick? Obviously, only one; all the items:
Anyway, how many sets of four could you pick? Obviously, only one; all the items:
<source lang=apl>
<syntaxhighlight lang=apl>
       4!4
       4!4
1
1
</source>
</syntaxhighlight>
=== Automatic mapping ===
=== Automatic mapping ===
A really nice feature of APL is its [[Array model|array]]-orientation. For computations which are defined on single elements ([[scalar functions]]), [[wikipedia:map (higher-order function)|map]]ping is implicit:
A really nice feature of APL is its [[Array model|array]]-orientation. For computations which are defined on single elements ([[scalar functions]]), [[wikipedia:map (higher-order function)|map]]ping is implicit:
<source lang=apl>
<syntaxhighlight lang=apl>
       0 1 2 3 4!4
       0 1 2 3 4!4
1 4 6 4 1
1 4 6 4 1
</source>
</syntaxhighlight>
(What's up with picking zero out of four items? Since all [[empty]] hands are equal, there is exactly one such set — the empty set.)
(What's up with picking zero out of four items? Since all [[empty]] hands are equal, there is exactly one such set — the empty set.)


== Order of evaluation ==
== Order of evaluation ==
We want to generate the indices using [[Iota]] (<source lang=apl inline>⍳</source>)…
We want to generate the indices using [[Iota]] (<syntaxhighlight lang=apl inline>⍳</syntaxhighlight>)…
<source lang=apl>      ⍳5!4
<syntaxhighlight lang=apl>      ⍳5!4


</source>
</syntaxhighlight>
That didn't work! This is because APL dispenses with traditional mathematics' confusing and inconsistent precedence order<ref>[[Ken Iverson|K.E. Iverson]], Appendix A: [https://www.jsoftware.com/papers/EvalOrder.htm Conventions Governing Order of Evaluation], Elementary Functions: An Algorithmic Treatment). Science Research Associates, 1966</ref>, replacing it with a simple right-to-left rule:
That didn't work! This is because APL dispenses with traditional mathematics' confusing and inconsistent [[precedence]] order<ref>[[Ken Iverson|K.E. Iverson]], Appendix A: [https://www.jsoftware.com/papers/EvalOrder.htm Conventions Governing Order of Evaluation], Elementary Functions: An Algorithmic Treatment). Science Research Associates, 1966</ref>, replacing it with a simple right-to-left rule:
<source lang=apl>
<syntaxhighlight lang=apl>
       (⍳5)!4
       (⍳5)!4
1 4 6 4 1
1 4 6 4 1
</source>
</syntaxhighlight>


== Swapping arguments ==
== Swapping arguments ==
If the arguments of <source lang=apl inline>!</source> were swapped, we wouldn't need that parenthesis. Enter the [[operator]] (higher-order function) [[swap]] (<source lang=apl inline>⍨</source>) which takes a [[dyadic]] function on its left and creates a new [[derived function]] which is identical to the original, but has swapped arguments:
If the arguments of <syntaxhighlight lang=apl inline>!</syntaxhighlight> were swapped, we wouldn't need that parenthesis. Enter the [[operator]] (higher-order function) [[swap]] (<syntaxhighlight lang=apl inline>⍨</syntaxhighlight>) which takes a [[dyadic]] function on its left and creates a new [[derived function]] which is identical to the original, but has swapped arguments:
<source lang=apl>
<syntaxhighlight lang=apl>
       4!⍨⍳5
       4!⍨⍳5
1 4 6 4 1
1 4 6 4 1
</source>
</syntaxhighlight>


== A number is a number ==
== A number is a number ==
The next step is to halve everything:
The next step is to halve everything:
<source lang=apl>
<syntaxhighlight lang=apl>
       .5×4!⍨⍳5
       .5×4!⍨⍳5
0.5 2 3 2 0.5
0.5 2 3 2 0.5
</source>
</syntaxhighlight>
Notice how we were dealing with integers until now, but then we [[multiply]] by a float (non-integer). In APL, you don't need to worry about numeric data type conversions. All numeric types get automatically promoted and demoted as needed. APL implementations will usually use the most compact internal representation.
Notice how we were dealing with integers until now, but then we [[multiply]] by a float (non-integer). In APL, you don't need to worry about numeric data type conversions. All numeric types get automatically promoted and demoted as needed. APL implementations will usually use the most compact internal representation.
=== Traditional mathematical symbols ===
=== Traditional mathematical symbols ===
Line 93: Line 93:
== Tables ==
== Tables ==
Remember the multiplication table from school?
Remember the multiplication table from school?
<source lang=apl>
<syntaxhighlight lang=apl>
       1 2 3 4 5∘.×1 2 3 4 5
       1 2 3 4 5∘.×1 2 3 4 5
1  2  3  4  5
1  2  3  4  5
Line 100: Line 100:
4  8 12 16 20
4  8 12 16 20
5 10 15 20 25
5 10 15 20 25
</source>
</syntaxhighlight>
Any function can be made into a table with the [[Outer Product]]:
Any function can be made into a table with the [[Outer Product]]:
<source lang=apl>
<syntaxhighlight lang=apl>
       1 2 3 4 5∘.+1 2 3 4 5
       1 2 3 4 5∘.+1 2 3 4 5
2 3 4 5  6
2 3 4 5  6
Line 109: Line 109:
5 6 7 8  9
5 6 7 8  9
6 7 8 9 10
6 7 8 9 10
</source>
</syntaxhighlight>
=== Using an argument twice ===
=== Using an argument twice ===
It gets tedious to type the same argument twice. Enter the [[self]]ie operator which shares its [[Glyph|symbol]] with the above-mentioned [[swap]] operator. There's no ambiguity here. ''Swap'' swaps the two arguments, while ''selfie'' uses a single argument twice:
It gets tedious to type the same argument twice. Enter the [[self]]ie operator which shares its [[Glyph|symbol]] with the above-mentioned [[swap]] operator. There's no ambiguity here. ''Swap'' swaps the two arguments, while ''selfie'' uses a single argument twice:
<source lang=apl>
<syntaxhighlight lang=apl>
       ∘.+⍨1 2 3 4 5
       ∘.+⍨1 2 3 4 5
2 3 4 5  6
2 3 4 5  6
Line 119: Line 119:
5 6 7 8  9
5 6 7 8  9
6 7 8 9 10
6 7 8 9 10
</source>
</syntaxhighlight>
We'll use this in our logo expression:
We'll use this in our logo expression:
<source lang=apl>
<syntaxhighlight lang=apl>
       ∘.+⍨.5×4!⍨⍳5
       ∘.+⍨.5×4!⍨⍳5
1  2.5 3.5 2.5 1   
1  2.5 3.5 2.5 1   
Line 128: Line 128:
2.5 4  5  4  2.5
2.5 4  5  4  2.5
1  2.5 3.5 2.5 1   
1  2.5 3.5 2.5 1   
</source>
</syntaxhighlight>
== Rounding ==
== Rounding ==
The last step is to round these numbers down. Traditional mathematics writes ''floor'' as <math>\lfloor x \rfloor</math> but APL is regular, so no function is denoted by two separated symbols. If the function takes a single argument, then the symbol will be on the left, so we write [[floor]] as <source lang=apl inline>⌊x</source>:
The last step is to round these numbers down. Traditional mathematics writes ''floor'' as <math>\lfloor x \rfloor</math> but APL is regular, so no function is denoted by two separated symbols. If the function takes a single argument, then the symbol will be on the left, so we write [[floor]] as <syntaxhighlight lang=apl inline>⌊x</syntaxhighlight>:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⌊∘.+⍨.5×4!⍨⍳5
       ⌊∘.+⍨.5×4!⍨⍳5
1 2 3 2 1
1 2 3 2 1
Line 138: Line 138:
2 4 5 4 2
2 4 5 4 2
1 2 3 2 1
1 2 3 2 1
</source>
</syntaxhighlight>
Those are our radii! Let's save them:
Those are our radii! Let's save them:
<source lang=apl>
<syntaxhighlight lang=apl>
       sizes←⌊∘.+⍨.5×4!⍨⍳5
       sizes←⌊∘.+⍨.5×4!⍨⍳5
</source>
</syntaxhighlight>


== Placing the circles ==
== Placing the circles ==
=== Pairwise summation ===
=== Pairwise summation ===
Now that we have our radii, we need to figure out where to put our circles. The horizontal pair-wise sum shows how much adjacent circles "reach out" towards each other. [[N-wise Reduce]] solves that. Here, <source lang=apl inline>/</source> is an operator which takes the plus function and applies it in-between the elements of each horizontal run of length ''N'' (the left argument) in the right argument:
Now that we have our radii, we need to figure out where to put our circles. The horizontal pair-wise sum shows how much adjacent circles "reach out" towards each other. [[N-wise Reduce]] solves that. Here, <syntaxhighlight lang=apl inline>/</syntaxhighlight> is an operator which takes the plus function and applies it in-between the elements of each horizontal run of length ''N'' (the left argument) in the right argument:
<source lang=apl>
<syntaxhighlight lang=apl>
       2+/sizes
       2+/sizes
3  5  5 3
3  5  5 3
Line 154: Line 154:
6  9  9 6
6  9  9 6
3  5  5 3
3  5  5 3
</source>
</syntaxhighlight>
=== Finding maxima ===
=== Finding maxima ===
Since the circles line up on a grid, we need the maximum for each horizontal space, that is for each column. APL uses [[dyad]]ic <source lang=apl inline>a⌈b</source> as the [[maximum]] of ''a'' and ''b''. <source lang=apl inline>⌈⌿</source> is the columnar maximum-[[reduce|reduction]]:
Since the circles line up on a grid, we need the maximum for each horizontal space, that is for each column. APL uses [[dyad]]ic <syntaxhighlight lang=apl inline>a⌈b</syntaxhighlight> as the [[maximum]] of ''a'' and ''b''. <syntaxhighlight lang=apl inline>⌈⌿</syntaxhighlight> is the columnar maximum-[[reduce|reduction]]:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⌈⌿2+/sizes
       ⌈⌿2+/sizes
8 11 11 8
8 11 11 8
</source>
</syntaxhighlight>
But obviously, we can't let the circles touch, so we add 1. This gives us all the centre-to-centre distances from the neighbours on the left (or above):
But obviously, we can't let the circles touch, so we add 1. This gives us all the centre-to-centre distances from the neighbours on the left (or above):
<source lang=apl>
<syntaxhighlight lang=apl>
       1+⌈⌿2+/sizes
       1+⌈⌿2+/sizes
9 12 12 9
9 12 12 9
</source>
</syntaxhighlight>
We also need an "offset" of the left/top-most circles which don't have any neighbours, so we prepend a 0:
We also need an "offset" of the left/top-most circles which don't have any neighbours, so we prepend a 0:
<source lang=apl>
<syntaxhighlight lang=apl>
       0,1+⌈⌿2+/sizes
       0,1+⌈⌿2+/sizes
0 9 12 12 9
0 9 12 12 9
</source>
</syntaxhighlight>
Finally, we compute the total offset for each column/row by finding the running total of the offsets:
Finally, we compute the total offset for each column/row by finding the running total of the offsets:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕←offsets←0,+\1+⌈⌿2+/sizes
       ⎕←offsets←0,+\1+⌈⌿2+/sizes
0 9 21 33 42
0 9 21 33 42
</source>
</syntaxhighlight>
<source lang=apl inline></source> is the [[identity]] function, which is just used here get the pass-though value from the assignment, as it would otherwise be hidden. (We call assignment ''[[shy]]''.) <source lang=apl inline>\</source> is just like <source lang=apl inline>/</source> but gives us the intermediate values.
Assigning to the special ''Quad character'' (the stylised console, <syntaxhighlight lang=apl inline></syntaxhighlight>) sends the the pass-though value from the assignment to the console (the screen), as it would otherwise be hidden. (We call assignment ''[[shy]]''.) <syntaxhighlight lang=apl inline>\</syntaxhighlight> is just like <syntaxhighlight lang=apl inline>/</syntaxhighlight> but gives us the intermediate values.


=== Combining arrays ===
=== Combining arrays ===
We need our offsets in two dimensions. So we need to combine the elements of <source lang=apl inline>offset</source> with themselves in all possible combinations.
We need our offsets in two dimensions. So we need to combine the elements of <syntaxhighlight lang=apl inline>offset</syntaxhighlight> with themselves in all possible combinations.


The [[Rank operator]] (<source lang=apl inline>⍤</source>) allows you to specify what you want paired up with what. In our case, we want individual numbers (which have zero [[axes]]) paired up with other individual numbers. As pairing up the numbers in <source lang=apl inline>3 1 4</source> with those in <source lang=apl inline>2 7 1</source>:
The [[Rank operator]] (<syntaxhighlight lang=apl inline>⍤</syntaxhighlight>) allows you to specify what you want paired up with what. In our case, we want individual numbers (which have zero [[axes]]) paired up with other individual numbers. As pairing up the numbers in <syntaxhighlight lang=apl inline>3 1 4</syntaxhighlight> with those in <syntaxhighlight lang=apl inline>2 7 1</syntaxhighlight>:
<source lang=apl>
<syntaxhighlight lang=apl>
       3 1 4(,⍤0)2 7 1
       3 1 4(,⍤0)2 7 1
3 2
3 2
1 7
1 7
4 1
4 1
</source>
</syntaxhighlight>
But we want to pair up each of the individual offsets with each of all the offsets. The offsets form a list, so we want to apply this pair-wise pairing function between each number and the entire list, as in:
But we want to pair up each of the individual offsets with each of all the offsets. The offsets form a list, so we want to apply this pair-wise pairing function between each number and the entire list, as in:
<source lang=apl>
<syntaxhighlight lang=apl>
       3 1 4(,⍤0⍤0 1)2 7 1
       3 1 4(,⍤0⍤0 1)2 7 1
3 2
3 2
Line 202: Line 202:
4 7
4 7
4 1
4 1
</source>
</syntaxhighlight>
Since we want the offsets paired up with themselves, we can use <source lang=apl inline>⍨</source> again:
Since we want the offsets paired up with themselves, we can use <syntaxhighlight lang=apl inline>⍨</syntaxhighlight> again:
<source lang=apl>
<syntaxhighlight lang=apl>
       ,⍤0⍤0 1⍨⍳3
       ,⍤0⍤0 1⍨⍳3
0 0
0 0
Line 217: Line 217:
2 1
2 1
2 2
2 2
</source>
</syntaxhighlight>
The offset pairs form our circle locations:
The offset pairs form our circle locations:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⍴locs←,⍤0⍤0 1⍨offsets
       ⍴locs←,⍤0⍤0 1⍨offsets
5 5 2
5 5 2
</source>
</syntaxhighlight>
that is, it is a 3D array with 5 layers, 5 rows in each layer, and 2 columns in each row. Each row of our result represents an <math>(x,y)</math> value. The first and last layers, which represents the leftmost and rightmost columns of the logo, are:
that is, it is a 3D array with 5 layers, 5 rows in each layer, and 2 columns in each row. Each row of our result represents an <math>(x,y)</math> value. The first and last layers, which represents the leftmost and rightmost columns of the logo, are:
<source lang=apl>
<syntaxhighlight lang=apl>
       (1↑locs) (¯1↑locs)
       (1↑locs) (¯1↑locs)
┌────┬─────┐
┌────┬─────┐
Line 233: Line 233:
│0 42│42 42│
│0 42│42 42│
└────┴─────┘
└────┴─────┘
</source>
</syntaxhighlight>
For example, the second row in the first layer is <math>(x,y)=(0,9)</math>. <source lang=apl inline>↑</source> is the [[Take]] function, that is, it takes the first ''N'' cells of an array, and a negative value simply means taking from the rear.
For example, the second row in the first layer is <math>(x,y)=(0,9)</math>. <syntaxhighlight lang=apl inline>↑</syntaxhighlight> is the [[Take]] function, that is, it takes the first ''N'' cells of an array, and a negative value simply means taking from the rear.


== Making <code><circle/></code> tags ==
== Making <code><circle/></code> tags ==
To help us create our SVG <code><circle/></code> tags, well set up a couple of helper functions. The first function will help us create tag attributes.
To help us create our SVG <code><circle/></code> tags, well set up a couple of helper functions. The first function will help us create tag attributes.
=== Formatting attributes ===
=== Formatting attributes ===
APL uses a [[high minus]] (<source lang=apl inline>¯</source>), to indicate that a number is negative. This avoids confusion with the [[negate]] function <source lang=apl inline>-</source>. However, SVG uses a regular dash, so we need to change our numeric arrays into character representations (using the [[format]] function, <source lang=apl inline>⍕</source>), and [[replace]] all occurrences of the symbol:
APL uses a [[high minus]] (<syntaxhighlight lang=apl inline>¯</syntaxhighlight>), to indicate that a number is negative. This avoids confusion with the [[negate]] function <syntaxhighlight lang=apl inline>-</syntaxhighlight>. However, SVG uses a regular dash, so we need to change our numeric arrays into character representations (using the [[format]] function, <syntaxhighlight lang=apl inline>⍕</syntaxhighlight>), and [[replace]] all occurrences of the symbol:
<source lang=apl>
<syntaxhighlight lang=apl>
       '¯'⎕R'-'⍕3 ¯1 2 ¯7
       '¯'⎕R'-'⍕3 ¯1 2 ¯7
3 -1 2 -7
3 -1 2 -7
</source>
</syntaxhighlight>
The attribute value needs to be quoted, so we [[catenate|prepend]] (<source lang=apl inline>,</source>) two quotation marks, and then we [[rotate]] the text one step (left), thereby pushing the first one to the end:
The attribute value needs to be quoted, so we [[catenate|prepend]] (<syntaxhighlight lang=apl inline>,</syntaxhighlight>) two quotation marks, and then we [[rotate]] the text one step (left), thereby pushing the first one to the end:
<source lang=apl>
<syntaxhighlight lang=apl>
       1⌽'""','¯'⎕R'-'⍕3 ¯1 2 ¯7
       1⌽'""','¯'⎕R'-'⍕3 ¯1 2 ¯7
"3 -1 2 -7"
"3 -1 2 -7"
</source>
</syntaxhighlight>
Finally, create the full attribute phrase:
Finally, create the full attribute phrase:
<source lang=apl>
<syntaxhighlight lang=apl>
       ' ','test','=',1⌽'""','¯'⎕R'-'⍕3 ¯1 2 ¯7
       ' ','test','=',1⌽'""','¯'⎕R'-'⍕3 ¯1 2 ¯7
  test="3 -1 2 -7"
  test="3 -1 2 -7"
</source>
</syntaxhighlight>
=== Our first function ===
=== Our first function ===
In the most basic form, a [[dfn]] ("dee fun") is just an expression in curly braces with <source lang=apl inline>⍺</source> and <source lang=apl inline>⍵</source> representing the left and right arguments, just like they are the leftmost and rightmost letters of the [[wikipedia:Greek alphabet|Greek alphabet]]:
In the most basic form, a [[dfn]] ("dee fun") is just an expression in curly braces with <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> and <syntaxhighlight lang=apl inline>⍵</syntaxhighlight> representing the left and right arguments, just like they are the leftmost and rightmost letters of the [[wikipedia:Greek alphabet|Greek alphabet]]:
<source lang=apl>
<syntaxhighlight lang=apl>
       Attr←{' ',⍺,'=',1⌽'""','¯'⎕R'-'⍕⍵}
       Attr←{' ',⍺,'=',1⌽'""','¯'⎕R'-'⍕⍵}
       'test' Attr 3 ¯1 2 ¯7
       'test' Attr 3 ¯1 2 ¯7
  test="3 -1 2 -7"
  test="3 -1 2 -7"
</source>
</syntaxhighlight>
Notice that assignment works for functions too! Remember the [[#Automatic_mapping|automatic mapping]]? It doesn't apply to user-defined functions, but we can use the [[Each]] operator (<source lang=apl inline>¨</source>) instead:
Notice that assignment works for functions too! Remember the [[#Automatic_mapping|automatic mapping]]? It doesn't apply to user-defined functions, but we can use the [[Each]] operator (<syntaxhighlight lang=apl inline>¨</syntaxhighlight>) instead:
<source lang=apl>
<syntaxhighlight lang=apl>
       'test' 'foo' Attr¨ 3 ¯1
       'test' 'foo' Attr¨ 3 ¯1
┌─────────┬─────────┐
┌─────────┬─────────┐
│ test="3"│ foo="-1"│
│ test="3"│ foo="-1"│
└─────────┴─────────┘
└─────────┴─────────┘
</source>
</syntaxhighlight>


=== Flattening enclosed things and enclosing flat things ===
=== Flattening enclosed things and enclosing flat things ===
Next, we make this list of strings (each of which is actually just a character list) into a simple list with the [[enlist]] function (<source lang=apl inline>∊</source>), and use above same concatenation and rotation techniques to finalise our tag:
Next, we make this list of strings (each of which is actually just a character list) into a simple list with the [[enlist]] function (<syntaxhighlight lang=apl inline>∊</syntaxhighlight>), and use above same concatenation and rotation techniques to finalise our tag:
<source lang=apl>
<syntaxhighlight lang=apl>
       2⌽'/><tag',∊'test' 'foo'Attr¨ 3 ¯1
       2⌽'/><tag',∊'test' 'foo'Attr¨ 3 ¯1
<tag test="3" foo="-1"/>
<tag test="3" foo="-1"/>
</source>
</syntaxhighlight>
Let's create a dfn for that too:
Let's create a dfn for that too:
<source lang=apl>
<syntaxhighlight lang=apl>
       Circle←{⊂2⌽'/><circle',∊'cx' 'cy' 'r'Attr¨⍵}
       Circle←{⊂2⌽'/><circle',∊'cx' 'cy' 'r'Attr¨⍵}
       Circle 3 1 4
       Circle 3 1 4
Line 282: Line 282:
│<circle cx="3" cy="1" r="4"/>│
│<circle cx="3" cy="1" r="4"/>│
└─────────────────────────────┘
└─────────────────────────────┘
</source>
</syntaxhighlight>
Notice that a put in <source lang=apl inline>⊂</source>) which [[enclose]]s the result. This is because I want to deal with these tags as [[scalar]] elements. Now we can create our circles (and show the first three:
Notice that a put in <syntaxhighlight lang=apl inline>⊂</syntaxhighlight>) which [[enclose]]s the result. This is because I want to deal with these tags as [[scalar]] elements. Now we can create our circles (and show the first three:
<source lang=apl>
<syntaxhighlight lang=apl>
       3↑circles←,Circle⍤1⊢locs,sizes
       3↑circles←,Circle⍤1⊢locs,sizes
┌─────────────────────────────┬─────────────────────────────┬──────────────────────────────┐
┌─────────────────────────────┬─────────────────────────────┬──────────────────────────────┐
│<circle cx="0" cy="0" r="1"/>│<circle cx="0" cy="9" r="2"/>│<circle cx="0" cy="21" r="3"/>│
│<circle cx="0" cy="0" r="1"/>│<circle cx="0" cy="9" r="2"/>│<circle cx="0" cy="21" r="3"/>│
└─────────────────────────────┴─────────────────────────────┴──────────────────────────────┘
└─────────────────────────────┴─────────────────────────────┴──────────────────────────────┘
</source>
</syntaxhighlight>
Here we're using <source lang=apl inline>⍤</source> again, but this time only with a single argument, so we only specify one rank — the rank of the sub-arrays we want <source lang=apl inline>Circle</source> called on. Rows are vector (list), so that's rank 1. The <source lang=apl inline>⊢</source> doesn't do anything other than separate <source lang=apl inline>1</source> from <source lang=apl inline>locs</source>, so the <source lang=apl inline>⍤</source> knows what is its right operand (which specifies on what sub-arrays the function left [[operand]] is to be called) and what is its right [[argument]] (which contains the arguments for that function in its sub-arrays).
Here we're using <syntaxhighlight lang=apl inline>⍤</syntaxhighlight> again, but this time only with a single argument, so we only specify one rank — the rank of the sub-arrays we want <syntaxhighlight lang=apl inline>Circle</syntaxhighlight> called on. Rows are vector (list), so that's rank 1. The <syntaxhighlight lang=apl inline>⊢</syntaxhighlight> doesn't do anything other than separate <syntaxhighlight lang=apl inline>1</syntaxhighlight> from <syntaxhighlight lang=apl inline>locs</syntaxhighlight>, so the <syntaxhighlight lang=apl inline>⍤</syntaxhighlight> knows what is its right operand (which specifies on what sub-arrays the function left [[operand]] is to be called) and what is its right [[argument]] (which contains the arguments for that function in its sub-arrays).


[[Monad]]ic <source lang=apl inline>,</source> is called [[Ravel]] as it unravels an array into a vector, but unlike <source lang=apl inline>∊</source> it doesn't flatten enclosed elements.
[[Monad]]ic <syntaxhighlight lang=apl inline>,</syntaxhighlight> is called [[Ravel]] as it unravels an array into a vector, but unlike <syntaxhighlight lang=apl inline>∊</syntaxhighlight> it doesn't flatten enclosed elements.


== The <code><svg></code> container ==
== The <code><svg></code> container ==
[[APL Wiki]] is a [[wikipedia:MediaWiki|MediaWiki]] which has strict requirements on the dimensions of the site logo:
[[APL Wiki]] is a [[wikipedia:MediaWiki|MediaWiki]] which has strict requirements on the dimensions of the site logo:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕←dims←∊'width' 'height'Attr¨130
       ⎕←dims←∊'width' 'height'Attr¨130
  width="130" height="130"
  width="130" height="130"
</source>
</syntaxhighlight>
Since the circles on the first row and in the first column are at position 0, we need our SVG to begin a bit further to the top left. Similarly, it needs to end a bit further to the bottom right than the last circle. How much? Well, the maximum radius (which would extend up and to the left) in the first row (and column) is:
Since the circles on the first row and in the first column are at position 0, we need our SVG to begin a bit further to the top left. Similarly, it needs to end a bit further to the bottom right than the last circle. How much? Well, the maximum radius (which would extend up and to the left) in the first row (and column) is:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⌈/1↑sizes
       ⌈/1↑sizes
3
3
</source>
</syntaxhighlight>
But let's add a bit more so the circles don't touch the image edge:
But let's add a bit more so the circles don't touch the image edge:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕←pad←2+⌈/1↑sizes
       ⎕←pad←2+⌈/1↑sizes
5
5
</source>
</syntaxhighlight>
=== Taking things to a higher dimension ===
=== Taking things to a higher dimension ===
Finding the centres of the first and last circles with <source lang=apl inline>Circle</source> but this time with a 2-element left argument (this [[take]]s the first 1 layer and the first 1 row of that, etc.), we can find out where to begin, and the size of our image, which makes up the "viewBox" attribute:
Finding the centres of the first and last circles with <syntaxhighlight lang=apl inline>Circle</syntaxhighlight> but this time with a 2-element left argument (this [[take]]s the first 1 layer and the first 1 row of that, etc.), we can find out where to begin, and the size of our image, which makes up the "viewBox" attribute:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕←first←1 1↑locs
       ⎕←first←1 1↑locs
0 0
0 0
Line 323: Line 323:
       ⎕←viewBox←'viewBox'Attr,begin,size
       ⎕←viewBox←'viewBox'Attr,begin,size
  viewBox="-5 -5 52 52"
  viewBox="-5 -5 52 52"
</source>
</syntaxhighlight>
The extra ravel <source lang=apl inline>,</source> is because <source lang=apl inline>begin</source> and <source lang=apl inline>size</source> are matrices, while <source lang=apl inline>Attr</source> needs a vector.
The extra ravel <syntaxhighlight lang=apl inline>,</syntaxhighlight> is because <syntaxhighlight lang=apl inline>begin</syntaxhighlight> and <syntaxhighlight lang=apl inline>size</syntaxhighlight> are matrices, while <syntaxhighlight lang=apl inline>Attr</syntaxhighlight> needs a vector.


Now we construct the opening tag:
Now we construct the opening tag:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕←svg←'<svg',dims,viewBox,' xmlns="http://www.w3.org/2000/svg">'
       ⎕←svg←'<svg',dims,viewBox,' xmlns="http://www.w3.org/2000/svg">'
<svg width="130" height="130" viewBox="-5 -5 52 52" xmlns="http://www.w3.org/2000/svg">
<svg width="130" height="130" viewBox="-5 -5 52 52" xmlns="http://www.w3.org/2000/svg">
</source>
</syntaxhighlight>
=== In-place modification through assignment ===
=== In-place modification through assignment ===
We can do in-place concatenations to add all the circle tags and the closing tag:
We can do in-place concatenations to add all the circle tags and the closing tag:
<source lang=apl>
<syntaxhighlight lang=apl>
      svg,←∊circles
       100↑svg,←∊circles
       100↑svg,←∊circles
<circle cx="0" cy="0" r="1"/><circle cx="0" cy="9" r="2"/><circle cx="0" cy="21" r="3"/><circle cx="
<circle cx="0" cy="0" r="1"/><circle cx="0" cy="9" r="2"/><circle cx="0" cy="21" r="3"/><circle cx="
       svg,←'</svg>'
       svg,←'</svg>'
</source>
</syntaxhighlight>
You may recognise the pattern here as [[wikipedia:augmented assignment|augmented assignment]] from C and related programming languages. APL allows you to use any function to modify values in-place. If you are not familiar with this, then just think of <source lang=apl inline>name,←value</source> as <source lang=apl inline>name←name,value</source> (but the pass-though value is whatever is on the right of the assignment arrow).
You may recognise the pattern here as [[wikipedia:augmented assignment|augmented assignment]] from C and related programming languages. APL allows you to use any function to modify values in-place. If you are not familiar with this, then just think of <syntaxhighlight lang=apl inline>name,←value</syntaxhighlight> as <syntaxhighlight lang=apl inline>name←name,value</syntaxhighlight> (but the pass-though value is whatever is on the right of the assignment arrow).


== Doing something with what we made ==
== Doing something with what we made ==
Now we we can put the SVG data into a native (OS) file:
Now we we can put the SVG data into a native (OS) file:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕←svg ⎕NPUT '/tmp/aplwiki.svg'
       ⎕←svg ⎕NPUT '/tmp/aplwiki.svg'
850
850
</source>
</syntaxhighlight>
The result is the number of bytes written (which can vary due to line ending standards). Alternatively, flatten the svg and we can show it in an embedded HTML window:
The result is the number of bytes written (which can vary due to line ending standards). Alternatively, flatten the svg and we can show it in an embedded HTML window:
<source lang=apl>
<syntaxhighlight lang=apl>
       ]html svg
       ]html svg
</source>
</syntaxhighlight>
[[File:)HTML rendering APL Wiki logo.png|frameless|]HTML rendering APL Wiki logo]]
[[File:)HTML rendering APL Wiki logo.png|frameless|]HTML rendering APL Wiki logo]]
== Code ==
== Code ==
{{Collapse|The function below contains all the code from above. It takes a file name as argument. (Please note one user of Dyalog 16.0 found it necessary to enclose the "svg" argument on the last line).|
{{Collapse|The function below contains all the code from above. It takes a file name as argument.|
<source lang=apl>
<syntaxhighlight lang=apl>
  Logo←{
  Logo←{
     sizes←⌊∘.+⍨0.5×4!⍨⍳5
     sizes←⌊∘.+⍨0.5×4!⍨⍳5
Line 373: Line 372:


     viewBox←'viewBox'Attr,begin,size
     viewBox←'viewBox'Attr,begin,size
     svg←⊂'<svg',dims,viewBox,' xmlns="http://www.w3.org/2000/svg">'
     svg←'<svg',dims,viewBox,' xmlns="http://www.w3.org/2000/svg">'
     svg,←circles,'</svg>'
     svg,←∊circles
    svg,'</svg>'
     svg ⎕NPUT ⍵
     svg ⎕NPUT ⍵
  }
  }
</source>
</syntaxhighlight>
}}
}}



Latest revision as of 21:38, 10 September 2022

APL Wiki logo

The APL Wiki logo consists of the following numeric matrix, where each number indicates a circle radius:

      ⎕IO←0
      ⌊∘.+⍨.5×4!⍨⍳5
1 2 3 2 1
2 4 5 4 2
3 5 6 5 3
2 4 5 4 2
1 2 3 2 1

This page explains, step-by-step, how we generate our SVG logo, using the above expression[1]. This demonstrates quite a few APL features.

We will follow APL's evaluation from right to left.

Counting

From 1 or from 0?

A computer console:

Whether to count from 0 or from 1 is an old disagreement among programmers. Many APLs let you choose whichever convention you want, but they tend to use 1 by default. To switch convention, we set the variable ⎕IO:

      ⎕IO←0

By the way, IO stands for Index Origin.

We can already now observe a couple of APL's characteristics…

No reserved words

The name ⎕IO begins with the special Quad character (a stylised console) which symbolises the computer system itself. APL has no reserved words. Rather, all built-in constants, variables, functions and operators have the prefix indicating that they are part of the system. Because of this, we call them quad names.

Assignments

Assignment is not done with = like in many other programming languages, but rather with which also indicates the direction of the assignment: Whatever is on the right gets put into the name on the left.

Generating indices

The function takes a number N and generates indices until is has made N indices. Since we set ⎕IO to 0, we count from 0 until right before N:

      ⍳5
0 1 2 3 4

How many subsets?

Consider a bag with four distinct items. If you stick your hand into the bag and pick two items out, how many different possibilities are there for which pair you get out? . APL can tell you this with the Binomial (!) function:

      2!4
6

Notice how APL uses traditional mathematical symbols in a generalised way. The traditional post-fix (after its argument) symbol is used with a syntax similar to how you'd normally use or . In fact, all APL functions can be used infix, like or prefix, like .

Anyway, how many sets of four could you pick? Obviously, only one; all the items:

      4!4
1

Automatic mapping

A really nice feature of APL is its array-orientation. For computations which are defined on single elements (scalar functions), mapping is implicit:

      0 1 2 3 4!4
1 4 6 4 1

(What's up with picking zero out of four items? Since all empty hands are equal, there is exactly one such set — the empty set.)

Order of evaluation

We want to generate the indices using Iota ()…

      ⍳5!4


That didn't work! This is because APL dispenses with traditional mathematics' confusing and inconsistent precedence order[2], replacing it with a simple right-to-left rule:

      (⍳5)!4
1 4 6 4 1

Swapping arguments

If the arguments of ! were swapped, we wouldn't need that parenthesis. Enter the operator (higher-order function) swap () which takes a dyadic function on its left and creates a new derived function which is identical to the original, but has swapped arguments:

      4!⍨⍳5
1 4 6 4 1

A number is a number

The next step is to halve everything:

      .5×4!⍨⍳5
0.5 2 3 2 0.5

Notice how we were dealing with integers until now, but then we multiply by a float (non-integer). In APL, you don't need to worry about numeric data type conversions. All numeric types get automatically promoted and demoted as needed. APL implementations will usually use the most compact internal representation.

Traditional mathematical symbols

Also notice that we use a proper multiplication symbol, , for multiplication. If traditional mathematics has a symbol for a concept APL includes then APL will use that symbol. Another example is for division.

Tables

Remember the multiplication table from school?

      1 2 3 4 5∘.×1 2 3 4 5
1  2  3  4  5
2  4  6  8 10
3  6  9 12 15
4  8 12 16 20
5 10 15 20 25

Any function can be made into a table with the Outer Product:

      1 2 3 4 5∘.+1 2 3 4 5
2 3 4 5  6
3 4 5 6  7
4 5 6 7  8
5 6 7 8  9
6 7 8 9 10

Using an argument twice

It gets tedious to type the same argument twice. Enter the selfie operator which shares its symbol with the above-mentioned swap operator. There's no ambiguity here. Swap swaps the two arguments, while selfie uses a single argument twice:

      ∘.+⍨1 2 3 4 5
2 3 4 5  6
3 4 5 6  7
4 5 6 7  8
5 6 7 8  9
6 7 8 9 10

We'll use this in our logo expression:

      ∘.+⍨.5×4!⍨⍳5
1   2.5 3.5 2.5 1  
2.5 4   5   4   2.5
3.5 5   6   5   3.5
2.5 4   5   4   2.5
1   2.5 3.5 2.5 1

Rounding

The last step is to round these numbers down. Traditional mathematics writes floor as but APL is regular, so no function is denoted by two separated symbols. If the function takes a single argument, then the symbol will be on the left, so we write floor as ⌊x:

      ⌊∘.+⍨.5×4!⍨⍳5
1 2 3 2 1
2 4 5 4 2
3 5 6 5 3
2 4 5 4 2
1 2 3 2 1

Those are our radii! Let's save them:

      sizes←⌊∘.+⍨.5×4!⍨⍳5

Placing the circles

Pairwise summation

Now that we have our radii, we need to figure out where to put our circles. The horizontal pair-wise sum shows how much adjacent circles "reach out" towards each other. N-wise Reduce solves that. Here, / is an operator which takes the plus function and applies it in-between the elements of each horizontal run of length N (the left argument) in the right argument:

      2+/sizes
3  5  5 3
6  9  9 6
8 11 11 8
6  9  9 6
3  5  5 3

Finding maxima

Since the circles line up on a grid, we need the maximum for each horizontal space, that is for each column. APL uses dyadic a⌈b as the maximum of a and b. ⌈⌿ is the columnar maximum-reduction:

      ⌈⌿2+/sizes
8 11 11 8

But obviously, we can't let the circles touch, so we add 1. This gives us all the centre-to-centre distances from the neighbours on the left (or above):

      1+⌈⌿2+/sizes
9 12 12 9

We also need an "offset" of the left/top-most circles which don't have any neighbours, so we prepend a 0:

      0,1+⌈⌿2+/sizes
0 9 12 12 9

Finally, we compute the total offset for each column/row by finding the running total of the offsets:

      ⎕←offsets←0,+\1+⌈⌿2+/sizes
0 9 21 33 42

Assigning to the special Quad character (the stylised console, ) sends the the pass-though value from the assignment to the console (the screen), as it would otherwise be hidden. (We call assignment shy.) \ is just like / but gives us the intermediate values.

Combining arrays

We need our offsets in two dimensions. So we need to combine the elements of offset with themselves in all possible combinations.

The Rank operator () allows you to specify what you want paired up with what. In our case, we want individual numbers (which have zero axes) paired up with other individual numbers. As pairing up the numbers in 3 1 4 with those in 2 7 1:

      3 1 4(,⍤0)2 7 1
3 2
1 7
4 1

But we want to pair up each of the individual offsets with each of all the offsets. The offsets form a list, so we want to apply this pair-wise pairing function between each number and the entire list, as in:

      3 1 4(,⍤0⍤0 1)2 7 1
3 2
3 7
3 1
   
1 2
1 7
1 1
   
4 2
4 7
4 1

Since we want the offsets paired up with themselves, we can use again:

      ,⍤0⍤0 1⍨⍳3
0 0
0 1
0 2
   
1 0
1 1
1 2
   
2 0
2 1
2 2

The offset pairs form our circle locations:

      ⍴locs←,⍤0⍤0 1⍨offsets
5 5 2

that is, it is a 3D array with 5 layers, 5 rows in each layer, and 2 columns in each row. Each row of our result represents an value. The first and last layers, which represents the leftmost and rightmost columns of the logo, are:

      (1↑locs) (¯1↑locs)
┌────┬─────┐
│0  0│42  0│
│0  9│42  9│
│0 21│42 21│
│0 33│42 33│
│0 42│42 42│
└────┴─────┘

For example, the second row in the first layer is . is the Take function, that is, it takes the first N cells of an array, and a negative value simply means taking from the rear.

Making <circle/> tags

To help us create our SVG <circle/> tags, well set up a couple of helper functions. The first function will help us create tag attributes.

Formatting attributes

APL uses a high minus (¯), to indicate that a number is negative. This avoids confusion with the negate function -. However, SVG uses a regular dash, so we need to change our numeric arrays into character representations (using the format function, ), and replace all occurrences of the symbol:

      '¯'⎕R'-'⍕3 ¯1 2 ¯7
3 -1 2 -7

The attribute value needs to be quoted, so we prepend (,) two quotation marks, and then we rotate the text one step (left), thereby pushing the first one to the end:

      1⌽'""','¯'⎕R'-'⍕3 ¯1 2 ¯7
"3 -1 2 -7"

Finally, create the full attribute phrase:

      ' ','test','=',1⌽'""','¯'⎕R'-'⍕3 ¯1 2 ¯7
 test="3 -1 2 -7"

Our first function

In the most basic form, a dfn ("dee fun") is just an expression in curly braces with and representing the left and right arguments, just like they are the leftmost and rightmost letters of the Greek alphabet:

      Attr←{' ',⍺,'=',1⌽'""','¯'⎕R'-'⍕⍵}
      'test' Attr 3 ¯1 2 ¯7
 test="3 -1 2 -7"

Notice that assignment works for functions too! Remember the automatic mapping? It doesn't apply to user-defined functions, but we can use the Each operator (¨) instead:

      'test' 'foo' Attr¨ 3 ¯1
┌─────────┬─────────┐
│ test="3"│ foo="-1"│
└─────────┴─────────┘

Flattening enclosed things and enclosing flat things

Next, we make this list of strings (each of which is actually just a character list) into a simple list with the enlist function (), and use above same concatenation and rotation techniques to finalise our tag:

      2⌽'/><tag',∊'test' 'foo'Attr¨ 3 ¯1
<tag test="3" foo="-1"/>

Let's create a dfn for that too:

      Circle←{⊂2⌽'/><circle',∊'cx' 'cy' 'r'Attr¨⍵}
      Circle 3 1 4
┌─────────────────────────────┐
│<circle cx="3" cy="1" r="4"/>│
└─────────────────────────────┘

Notice that a put in ) which encloses the result. This is because I want to deal with these tags as scalar elements. Now we can create our circles (and show the first three:

      3↑circles←,Circle⍤1⊢locs,sizes
┌─────────────────────────────┬─────────────────────────────┬──────────────────────────────┐
│<circle cx="0" cy="0" r="1"/>│<circle cx="0" cy="9" r="2"/>│<circle cx="0" cy="21" r="3"/>│
└─────────────────────────────┴─────────────────────────────┴──────────────────────────────┘

Here we're using again, but this time only with a single argument, so we only specify one rank — the rank of the sub-arrays we want Circle called on. Rows are vector (list), so that's rank 1. The doesn't do anything other than separate 1 from locs, so the knows what is its right operand (which specifies on what sub-arrays the function left operand is to be called) and what is its right argument (which contains the arguments for that function in its sub-arrays).

Monadic , is called Ravel as it unravels an array into a vector, but unlike it doesn't flatten enclosed elements.

The <svg> container

APL Wiki is a MediaWiki which has strict requirements on the dimensions of the site logo:

      ⎕←dims←∊'width' 'height'Attr¨130
 width="130" height="130"

Since the circles on the first row and in the first column are at position 0, we need our SVG to begin a bit further to the top left. Similarly, it needs to end a bit further to the bottom right than the last circle. How much? Well, the maximum radius (which would extend up and to the left) in the first row (and column) is:

      ⌈/1↑sizes
3

But let's add a bit more so the circles don't touch the image edge:

      ⎕←pad←2+⌈/1↑sizes
5

Taking things to a higher dimension

Finding the centres of the first and last circles with Circle but this time with a 2-element left argument (this takes the first 1 layer and the first 1 row of that, etc.), we can find out where to begin, and the size of our image, which makes up the "viewBox" attribute:

      ⎕←first←1 1↑locs
0 0
      ⎕←last←¯1 ¯1↑locs
42 42
      ⎕←begin←first-pad
¯5 ¯5
      ⎕←size←(last-first)+2×pad
52 52
      ⎕←viewBox←'viewBox'Attr,begin,size
 viewBox="-5 -5 52 52"

The extra ravel , is because begin and size are matrices, while Attr needs a vector.

Now we construct the opening tag:

      ⎕←svg←'<svg',dims,viewBox,' xmlns="http://www.w3.org/2000/svg">'
<svg width="130" height="130" viewBox="-5 -5 52 52" xmlns="http://www.w3.org/2000/svg">

In-place modification through assignment

We can do in-place concatenations to add all the circle tags and the closing tag:

      100↑svg,←∊circles
<circle cx="0" cy="0" r="1"/><circle cx="0" cy="9" r="2"/><circle cx="0" cy="21" r="3"/><circle cx="
      svg,←'</svg>'

You may recognise the pattern here as augmented assignment from C and related programming languages. APL allows you to use any function to modify values in-place. If you are not familiar with this, then just think of name,←value as name←name,value (but the pass-though value is whatever is on the right of the assignment arrow).

Doing something with what we made

Now we we can put the SVG data into a native (OS) file:

      ⎕←svg ⎕NPUT '/tmp/aplwiki.svg'
850

The result is the number of bytes written (which can vary due to line ending standards). Alternatively, flatten the svg and we can show it in an embedded HTML window:

      ]html svg

]HTML rendering APL Wiki logo

Code

The function below contains all the code from above. It takes a file name as argument.
 Logo←{
     sizes←⌊∘.+⍨0.5×4!⍨⍳5
     offsets←0,+\1+⌈⌿2+/sizes
     indices←⍳⍴sizes
     locs←,⍤0⍤0 1⍨offsets

     Attr←{' ',⍺,'=',1⌽'""','¯'⎕R'-'⍕⍵}
     Circle←{⊂2⌽'/><circle',∊'cx' 'cy' 'r'Attr¨⍵}
     circles←,Circle⍤1⊢locs,sizes
     dims←∊'width' 'height'Attr¨130

     pad←2+⌈/0⌷sizes
     first←1 1↑locs
     last←¯1 ¯1↑locs
     begin←first-pad
     size←(last-first)+2×pad

     viewBox←'viewBox'Attr,begin,size
     svg←'<svg',dims,viewBox,' xmlns="http://www.w3.org/2000/svg">'
     svg,←∊circles
     svg,←'</svg>'
     svg ⎕NPUT ⍵
 }

References

  1. "Bubbler", message "52389201" in The Nineteenth Byte chat room. Stack Exchange network, 2019-10-31 23:57
  2. K.E. Iverson, Appendix A: Conventions Governing Order of Evaluation, Elementary Functions: An Algorithmic Treatment). Science Research Associates, 1966