It’s the month for Advent of Code! Perfect little programming problems, at least at first. I often try to solve some using different programming languages. I wasn’t particularly interested in solving any programming problems, but then I saw @codesections post their solutions using APL. This is the solution for day 2, apparently:
⎕IO←0 find_in←{ ⍺←0 0 t←⍺ t[1]+←1 ⍺[1]>99:⍺{t[0]+←1 ⋄ t[1]←¯1 ⋄ t find_in ⍵}⍵ in←⍵ in[0;1 2]←t[0 1] 19690720=0⌷intcode in:t t∇in } intcode←{ ⍺←0 one←{r←,⍵ ⋄ r[⍵[⍺;3]]←r[⍵[⍺;1]]+r[⍵[⍺;2]] ⋄ (⍺+1)intcode(⍴⍵)⍴r} two←{r←,⍵ ⋄ r[(⍵[⍺;3])]←r[⍵[⍺;1]]×r[⍵[⍺;2]] ⋄ (⍺+1)intcode(⍴⍵)⍴r} 1=0⌷⍵[⍺;]:⍺ one ⍵ 2=0⌷⍵[⍺;]:⍺ two ⍵ 99=0⌷⍵[⍺;]:,⍵ } parse←{{((⌈4÷⍨⍴⍵),4)⍴⍵}⍎¨','(≠⊆⊢)⍵} in_1←in←parse ⊃⊃⎕nget '02.input' 1 in_1[0;1 2]←12 2 pt1←0⌷intcode in_1 pt2←find_in in
It looks like it might break your brains! Apparently the key is maximal terseness. 🤯
Let me just quote this section from the Wikipedia page:
**Game of Life**
The following function “life”, written in Dyalog APL, takes a boolean matrix and calculates the new generation according to Conway’s Game of Life. It demonstrates the power of APL to implement a complex algorithm in very little code, but it is also very hard to follow unless one has advanced knowledge of APL.
`life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}`
Yes indeed...
Anyway, the lack of a special keyboard led me to the language J. The intro on Wikipedia says:
The J programming language, developed in the early 1990s by Kenneth E. Iverson and Roger Hui, is an array programming language based primarily on APL (also by Iverson).
To avoid repeating the APL special-character problem, J uses only the basic ASCII character set, resorting to the use of the dot and colon as inflections to form short words similar to digraphs.
All right! I’m going to use it to solve the day 1 problems after confirming that I know how to do it using Racket. As it turned out, the hardest part was reading a file where numbers are separated by newlines instead of spaces. Can you believe that? It took me two days to find a solution that didn’t involve splitting and joining the entire file.
The problem is that the following gives me boxed words and the newlines are also words:
;: 1!:1<'data'
The splitting and joining:
strsplit=: #@[ }.each [ (E. <;.1 ]) , strjoin=: #@[ }. <@[ ;@,. ] data =. > ;: ' ' strjoin LF strsplit 1!:1 <'data'
But today I discovered:
w =. ;: 1!:1 <'data' NB. a boxed array of words, numbers and \n i =. 2 * i. (# w) % 2 NB. the indexes of the numbers n =. ". > i { w NB. the list of numbers, opened
I do feel bad for constructing a list of indexes, first. Surely there’s an even better way to do this. In the code above, I’m building a list of indexes. The first line results in the following:
┌──┬─┬────┬─┐ │14│ │1969│ │ └──┴─┴────┴─┘
Here’s how to understand the second and third line:
Finally! 😓
A bit later I found another way:
w =. ;: 1!:1 <'data' NB. a boxed array of words n =. ". > ((#w) $ 1 0) # w NB. pick the odd ones, unbox, eval
And we’re back to where we were before. Slightly more elegant, I guess?
fuel =. verb define (<. y % 3) - 2 ) +/ fuel n
It really is terse. I need to use it some more. It feels like a language that forces you to think differently about the problems and I like that.
For the second day, I needed something to be called recursively. I took advantage of another feature of the J programming language: verbs can be monadic (taking one argument on the right, like `fuel 14`) or dyadic (taking two arguments, one on the left and one on the right). I wrote `total` such that calling it as `total 14` was the equivalent of calling it as `0 total 14`.
total =. 3 : 0 0 total y : f =. fuel y if. f <= 0 do. x else. x + f total f end. )
I feel dirty for using a conditional, haha. J is infecting my brains.
Here, take a look at these two pages. Apparently they are older and all the new documentation is maintained in a wiki, but still. Just staring at these pages was baffling.
On the other hand, wiki pages such as the one explaining why J has practically no loops – because all the primitives can act on arrays – makes me understand that Perl, my favorite language in all its weirdness did not come close to being as weird as this. All coding styles are possible in Perl but the culture went to object oriented programming instead of building on wantarray and making everything aware of “list context.” Too bad!
explaining why J has practically no loops
I guess if I wanted to recommend some pages to read in order, I’d say this:
1. Nu Voc is the concise cheat sheet followed by links to essays
2. Absolutely Essential Terms introduces you to words, nouns, verbs, ...
3. Array Processing shows you how you should write your code
4. Loopless explains how most loops disappear if your primitives are aware of arrays
#J #Programming #Advent of Code
(Please contact me if you want to remove your comment.)
⁂
I am not sure if I followed it correctly, but is the list parsing working even with, for example, two newlines?
– Peter Kotrčka 2019-12-10 03:28 UTC
---
You are right, for my code to work it is absolutely essential that there is a single newline between every number because what I’m doing is essentially picking out the odd elements.
I spent some time trying to implement a parser that just looks for digits, but failed. There’s an example of a sequential machine in their help pages, but I couldn’t get it simply emit a list of numbers.
– Alex Schroeder 2019-12-10 09:51 UTC