J

J is an array programming language. It is very, very different from the other programming languages I’ve encountered.

Take a look at my first post on J to get a feel for how I felt upon first encountering it: 2019-12-07 J Programming Language

2020-05-01 Island map generator and Text Mapper

OK, I’ve added code to my J program to print the map in Text Mapper format to stdout so that I can start taking a look at it. I added printing of a Text Mapper compatible output to my script... And I know how to generate the SVG from the command line... OK, let’s try!

The file I’m working on in JQt can be run from a terminal emulator.

jconsole '~user/temp/2.ijs'

To URL-encode the output, more or less:

jconsole '~user/temp/2.ijs'
| perl -p -e 's/ +$//ge;' -e 's/([^a-zA-Z0-9_.!~()-])/sprintf("%%%02X",ord($1))/ge'

So let’s store that in a variable:

map=$( \
  jconsole '~user/temp/2.ijs' \
  | perl -p -e 's/ +$//ge;' \
            -e 's/([^a-zA-Z0-9_.!~()-])/sprintf("%%%02X",ord($1))/ge')

This is what the viewer shows us:

Image 1

And now to call Text Mapper from the command line:

perl src/hex-mapping/text-mapper.pl get \
  --header 'Content-Type:application/x-www-form-urlencoded' \
  --method POST --content "map=$map&type=square" /render \
  2>/dev/null \
  > test.svg

Convert the SVG to PNG:

inkscape --without-gui --file=test.svg --export-png=test.png

There you go:

Image 2

Looks like the J preview does not use quite the same colours. Perhaps because of auto-scaling?

Also, hex maps. Different, of course, but still islands. Strangely familiar, but also … wrong … I think? In some weird way? If you don’t know the map using squares, it’s OK. But now that you do, it’s hard to think of this as some sort of torn land. Was there an earth quake that split of this little island on the right?

Image 3

I think what I can also say right now is that I need to work on the colour palette.

Here’s an image of the Galapagos, thanks to the visible earth project at NASA).

Galapagos

Here’s an image of Hawaii, thanks to the visible earth project at NASA).

Hawaii

I’m not sure I want to use these colours but clearly my impression on the ground on Seymore, where I felt everything was black or brown or rusty red does not match these green images from space.

I need to think about this some more.

Source Code

I really need to put it into a repository. 🙂

mx =: 30
my =: 20

NB. a table of complex numbers
c =: (i. my) j./ i. mx

NB. starting position of the hotspot
hr =: 5
hy =: >. hr % 2
hx =: <. 0.5 + (my % 3) + ? <. 0.5 + my % 3
hc =: hx j. hy

NB. a function to compute altitude changes based on where the hotspot is
change =: 3 : 0
  h =. hr > {. & *. c - y     NB. hotspot = 1
  u =. 0.8 < ? (my, mx) $ 0  NB. regions atop the hotspot might move up
  d =. 0.9 < ? (my, mx) $ 0  NB. regions off the hotspot might move down
  (u * h) - d * -. h
)

NB. a biased list of steps to take
d =: _1j1 1j1 0j1 0j1 0j1

NB. a table of altitudes
a =: (my, mx) $ 0

NB. compute the meandering path of the hotspot across the map
NB. compute the change for each step and add it to the altitude
NB. no negative values
3 : 0''
for. i. mx - 2  * hr do.
  hc =: hc+(?#d){d
  a =: 0 & >. a + change hc
end.
)

NB. print map for text-mapper
colors =: ('ocean', 'water', 'sand', 'dark-soil', 'soil', 'green', 'dark-green',: 'grey')
3 : 0''
for_i. i. my do.
  for_j. i. mx do.
    color =. (j { i { a) { colors
    smoutput (>'r<0>2.d' 8!:0 j) , (>'r<0>2.d' 8!:0 i) , ' ', color
  end.
end.
)
smoutput 'include https://campaignwiki.org/contrib/gnomeyland.txt'

NB. display map for visuals
decimal =:16"_#.'0123456789abcdef'"_ i.]

rgb =: 3 : 0
  n =. decimal }. y     NB. strip leading #
  (<.n % 65536), ((<.n % 256) |~ 256), (n |~ 256)
)
ocean  =: rgb '#1c86ee' NB. 0
water  =: rgb '#6ebae7' NB. 1
sand   =: rgb '#e3bea3' NB. 2
dry    =: rgb '#c97457' NB. 3
nice   =: rgb '#b0b446' NB. 4
green  =: rgb '#77904c' NB. 5
humid  =: rgb '#2d501a' NB. 6
rocky  =: rgb '#dcddbe' NB. 7
colors =: (ocean, water, sand, dry, nice, green, humid,: rocky)

load 'viewmat'
colors viewmat a
exit 0

Tags:

Add Comment

2020-04-25 Island generator using J

I’m trying to write an island generator in J. It’s hard. Every time I look at the documentation I’m close to tears. I feel so dumb. But like many hard things in life, if it works, suddenly you feel so smart. Now I feel smart. 😁

These were my notes:

Image 1 for 2020-04-25 Island generator using J

Here’s what the code looks like:

Image 2 for 2020-04-25 Island generator using J

That is:

mx =: 30
my =: 20

NB. a table of complex numbers
c =: (i. my) j./ i. mx

NB. a table of altitudes
a =: (my, mx) $ _2

NB. a table with the hotspot
hr =: 5
hy =: hr + 1
hx  =: <. 0.5 + (my % 3) + ? <. 0.5 + my % 3
hc =: hx j. hy
r =: ? (my, mx) $ 0
h =: 0.8 < r * hr > {. & *. c - hc

NB. a list of translations
d =: _1j0 0j1 0j1 1j0
s =: ( ? 30 # # d ) { d

NB. start loop
m =: m + h
3 : 0''
for_i. s do.
  hc =: hc + i
  r =: ? (my, mx) $ 0
  h =: 0.8 < r * 5 > {. & *. c - hc
  a =: a + h
end.
)

jv a

I know. It looks terrible. And m is no longer used.

Typical interactions whenever I want to try something in J look like this:

   ?3
1
   ?3
0
   ?3
2
   5 + ?5
9
   5 + ?5
7
   20/6+?20/6
|domain error
|   20/6+?    20/6
   20 / 6 + ? (20 / 6)
|domain error
|   20/6+?(    20/6)
   .i 4.5
|syntax error
|        .i 4.5
   i. 4.4
|domain error
|       i.4.4

Simple stuff works. Add a little bit of extra and it’s domain errors, syntax errors, spelling errors. 😭

It takes a while and then I remember: % divides, not /, or _2 is -2. Aaaaargh.

I also felt stumped when I had to write the loop. And now I … loop … over it? Help me J, you promised loop-less programming! If you know how to do it, please leave a comment.

Too bad that reading anything at all in the docs is taking me forever because it’s all in a foreign language, basically. Take a look at the reference section. Every time I look for something it takes me a long time to figure out what I’m actually looking for, and then it takes me a long time to understand what it says. But, slowly, I get the feeling that it might work.

And this is the current result, including the visualization:

Image 3 for 2020-04-25 Island generator using J

What I’m lacking now is the sinking of land as the hotspot moves away, and ‘cohesion’: land that is close to higher land on the hotspot is pulled along as it rises, land that is close to lower land off the hotspot is pulled along as neighbors sink.

As I look at the preview, I’m not even sure I need cohesion, actually. We’ll see!

Time passes...

Current status:

Image 4 for 2020-04-25 Island generator using J

mx =: 30
my =: 20

NB. a table of complex numbers
c =: (i. my) j./ i. mx

NB. a table of altitudes
a =: (my, mx) $ _2

NB. center of the hotspot
hr =: 5
hy =: >. hr % 2
hx  =: <. 0.5 + (my % 3) + ? <. 0.5 + my % 3
hc =: hx j. hy

NB. a list of translations
d =: _1j0 1j0, 4 $ 0j1
m =: ( ? 30 # # d ) { d

NB. start loop
3 : 0''
for_i. m do.
  hc =: hc + i               NB. move hotspot center
  h =: 5 > {. & *. c - hc    NB. hotspot = 1
  l =: -. _2 = a * -. h      NB. outside the hotspot -2 is the limit
  r =: 0.8 < ? (my, mx) $ 0  NB. 20% of regions are potentially active
  s =: 0.5 < ? (my, mx) $ 0  NB. 50% of regions...
  t =: h + s * -. h          NB. ...will ignore the activity
  a =: a + r * t* l * _1 + 2 * h
end.
)

jv a

Tags:

Comments on 2020-04-25 Island generator using J

Oh, and if you have J901, like I do on my laptop, then some changes are in order. First of all, jv doesn’t exist but viewmat does. Also, we can get rid of the explicit loop and define a dyadic verb instead, but then in order to call it, we need a boxed array where all the movements come first and the map at the end...

mx =: 30
my =: 20

NB. a table of complex numbers
c =: (i. my) j./ i. mx

NB. a table of altitudes
a =: (my, mx) $ _2

NB. center of the hotspot
hr =: 5
hy =: >. hr % 2
hx =: <. 0.5 + (my % 3) + ? <. 0.5 + my % 3
hc =: hx j. hy

NB. a list of translations
d =: _1j0 1j0, 4 $ 0j1
m =: ( ? 30 # # d ) { d

NB. function to run the simulation
NB. left is the map, right is the step to take
run =: dyad define
  hc =: hc + y               NB. move hotspot center
  h =. 5 > {. & *. c - hc    NB. hotspot = 1
  l =. -. _2 = x * -. h      NB. outside the hotspot -2 is the limit
  r =. 0.8 < ? (my, mx) $ 0  NB. 20% of regions are potentially active
  s =. 0.5 < ? (my, mx) $ 0  NB. 50% of regions...
  t =. h + s * -. h          NB. ...will ignore the activity
  x + r * t* l * _1 + 2 * h
)

NB. preparing arguments: a boxed array of all the translations and a at the end
args =: (;/m),<a
a    =: >run&.>~/args

viewmat a

Result:

An island

– Alex Schroeder 2020-04-28 16:16 UTC


Working on it! Now with the Gnomeyland palette!

mx =: 30
my =: 20

NB. a table of complex numbers
c =: (i. my) j./ i. mx

NB. starting position of the hotspot
hr =: 6
hy =: >. hr % 2
hx =: <. 0.5 + (my % 3) + ? <. 0.5 + my % 3
hc =: hx j. hy

NB. a function to compute altitude changes based on where the hotspot is
change =: 3 : 0
  h =. hr > {. & *. c - y     NB. hotspot = 1
  u =. 0.85 < ? (my, mx) $ 0  NB. regions atop the hotspot might move up
  d =. 0.7 < ? (my, mx) $ 0  NB. regions off the hotspot might move down
  (u * h) - d * -. h
)

NB. a biased list of steps to take
d =: _1j1 1j1 0j1

NB. a table of altitudes
a =: (my, mx) $ 0

NB. compute the meandering path of the hotspot across the map
NB. compute the change for each step and add it to the altitude
NB. no negative values
3 : 0''
for. i. mx - 2  * hr do.
  hc =: hc+(?#d){d
  a =: 0 & >. a + change hc
end.
)

decimal =:16"_#.'0123456789abcdef'"_ i.]

rgb =: 3 : 0
  n =. decimal }. y     NB. strip leading #
  (<.n % 65536), ((<.n % 256) |~ 256), (n |~ 256)
)
ocean  =: rgb '#1c86ee' NB. 0
water  =: rgb '#6ebae7' NB. 1
sand   =: rgb '#e3bea3' NB. 2
dry    =: rgb '#c97457' NB. 3
nice   =: rgb '#b0b446' NB. 4
green  =: rgb '#77904c' NB. 5
humid  =: rgb '#2d501a' NB. 6
rocky  =: rgb '#dcddbe' NB. 7
colors =: (ocean, water, sand, dry, nice, green, humid,: rocky)
colors viewmat a

Result:

Image 1

I still have to work on this. Something about rounding out the edges, perhaps. Bays filling up, exposed peninsulas getting razed, but lone islands left standing?

Perhaps I should think about generating Text Mapper output!

– Alex Schroeder 2020-04-28 20:41 UTC


Another idea: generate the path first, then adjust for the average y axis for better horizontal centring?

– Alex Schroeder 2020-04-28 20:50 UTC


Fun to see more new J enthusiasts popping up, I started becoming obsessed last december! Here’s a terser alternative for rgb for fun

rgb=: _2 (+/ .*&16 1)\ '0123456789abcdef'&i. @ }.

– Anonymous 2020-05-03 19:18 UTC


Thank you! 🙂

– Alex Schroeder 2020-05-03 19:29 UTC

Add Comment

2019-12-10 J parsing using a sequential machine

In a comment on my last post about the J programming language, Peter Kotrčka mentioned a flaw in the simple parsing trick I had used. I was basically assuming that the numbers in my input were separated by exactly one newline (or other J “word”).

I had spent some time trying to implement a parser that just looks for digits and ignores everything else, but failed. There’s an example of a sequential machine in their help pages, but I couldn’t get it to simply emit a list of numbers.

Peter made me return to that parser and I think I finally understood how it works! Thanks. 🙂

First, create an array mapping every input byte to a code. In this case, we create an array with 256 zeroes, and then we set the code to 1 for every digit.

m=: 256$0
m=: 1 (a.i.'0123456789')}m

The result looks something like this: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ... – as you can see there are a bunch of ones in there. 🙂

Next we need a state transition table. It has 3 dimensions.

  1. the first axis is the current state we’re in: 0 means we’re skipping stuff, 1 means we’re reading a number – we don’t need any more states
  2. the second axis is the current code we’re looking at: 0 means we’re looking at some byte that’s not a digit, 1 means we’re looking at a digit (this is where m is used)
  3. the third axis is simply two integers: the first one is the new state (we only have 0 or 1 to choose from), and the second one is what to do (the output code: 0 means doing nothing, 1 means start a word, 3 means emit a word and reset)

OK, so here’s how I built it:

NB.        state 0  state 1
s=: 2 2 2$ 0 0 1 1  0 3 1 0

Or graphically:

<"1 s
┌───┬───┐
│0 0│1 1│
├───┼───┤
│0 3│1 0│
└───┴───┘

Rows are the current state, columns are the input code we’re looking at.

Can you see it? I started thinking about it like this:

  1. given a string such as ’42 16’, starting at index 0, in state 0, with j being the beginning of a word pointing at -1 (in other words, no word is started) ...
  2. we look at ’4’ and get the new state from our m: 1 (as we’re looking at a digit)
  3. given this information, we need to look at the cell [1 1] (current state 0, input code 1)
  4. the new state is set to 1, and output code 1 means we begin a new word at the current index 0
  5. we look at ’2’ and get the new state from our m: still 1
  6. now we’re looking at the cell [1 0] (current state 1, input code 1)
  7. the new state continues set to 1, and output code 0 means nothing happens
  8. we look at the space character and get the new state from our m: 0
  9. now we’re looking at the cell [0 3] (current state 1, input code 0)
  10. the new state changes to 0, and output code 3 means we emit a word, starting from index 0 (that’s when we started it) up to the current position (”42”) and then we set j to -1 again

And so on.

The output code 3 means we emit a word and we do not begin a new word. This is important at the end: we could have used the output code 2 but that means we emit a word and begin a new one (j remains set), and the result is that any trailing garbage at the end is turned into a word.

And now the parser works for everything:

(0;s;m) ;: '42x16xx1'
┌──┬──┬─┐
│42│16│1│
└──┴──┴─┘

You might be wondering about the 0 at the beginning of that parser definition. The answer is that this tells the parser what to do with the words it finds. 0 means that the words end up in boxes. 2 means the word index and length, for example:

(2;s;m) ;: '42x16xx1'
0 2
3 2
7 1

At last I understand it!

Tags:

Comments on 2019-12-10 J parsing using a sequential machine

I installed j701 on the App Store.

I was also happy to learn:

J is written in portable C and is available for Windows, Linux, Mac, iOS, Android and Raspberry Pi. J can be installed and distributed for free. The source is provided under both commercial and GPL 3 licenses.

– Alex Schroeder 2019-12-13 00:31 UTC


When installing j901 on Debian, I had to install the following:

  • libqt5core5a
  • libqt5webkit5
  • libqt5websockets5
  • libqt5multimediawidgets5

I don’t know what I should have installed instead.

– Alex Schroeder 2019-12-13 17:55 UTC

Add Comment

2019-12-10 J madness continued

Ah, the madness continues... This is for day 2 of Advent of Code 2019.

  1. Your input is a list of numbers describing a program. The instructions consist of opcodes and some parameters. The opcode 99 has no parameters and indicates that the end has been reached. The opcode 1 means addition and 2 means multiplication. They both take three parameters, namely addresses into the same, 0-indexed list: the addresses of the two operands and the address of the result. The answer is the number in address 0 when the program ends.

What follows is an example input and how it changes with each step. The arrow is the current execution pointer.

01234567891011
191032311099304050
1910702311099304050
3500910702311099304050

First, let’s read data from the file and convert into a list of numbers. We use the same code as before, dumping the commas by simply picking the elements in the odd positions. This is our “original” program.

w =. ;: 1!:1 <'original'
o =: ". > ((#w) $ 1 0) # w

Then we need a function that takes the program, and an instruction pointer, executes it, modifies the program, and calls itself again, until we’re done.

This is not J-style array processing, sadly!

step =: dyad define
select. y { x
case. 1 do.
  a =. ((y+1){x){x
  b =. ((y+2){x){x
  c =. ((y+3){x)
  n =. (a + b) c } x
  n step y+4
case. 2 do.
  a =. ((y+1){x){x
  b =. ((y+2){x){x
  c =. ((y+3){x)
  n =. (a * b) c } x
  n step y+4
case. 99 do.
  0 { x
end.
)

Some notes:

  1. select. case. do. end. is similar to other languages
  2. 3 { x gets the third element from list x
  3. we need to do it twice, because the first { gives us the address we need to look at, and the second { gives us the value at that address
  4. 4 5 } x gives us a copy of x where the fifth value has been replaced by the number 4, which is why we need pass this modified value along to the next call

This computes the solution for part 1 of the challenge:

o step 0

What’s trickier to do is the second part. We are asked to “fix” the program by replacing the second and first number by all the numbers between 0 and 99 inclusive and find the pair that results in some magic value...

Here’s how to “fix” the program:

fix =. 1 2 }

And this is how you use it. The list to the left has to be exactly two elements long.

6 7 fix 1 2 3 4 5
1 6 7 4 5

Next we need a function that takes the original program, fixes is based on the two arguments, and computes the result using our step function.

try =. (4 : '((x, y) fix o) step 0')"0

Notable things that gave me headaches:

  1. I need to write (x, y) which is different from (6 7) because space-separated numbers are a thing of their own, where as space-separated identifiers are an application.
  2. I need “rank 0” (the part at the very end) such that the function gets called for every single pair of numbers in the end, and not on whole arrays.

Here’s one example of how to call it:

12 try 2

And now let’s create a 100×100 matrix and call try for every single pair:

try/~i.100

To pick this apart:

  1. i.100 creates a list of 0–99
  2. ~ is an “adverb” and modifies the preceding “verb” such that it gets called with two copies of it’s argument, so two lists of 0–99 instead of one ...
  3. / is an “adverb” and modifies the preceding “verb” such that it results in a table, getting called for every a in x and every b in y ...
  4. try is our “verb” – so in effect we’re calling 0 try 0, 0 try 1, ... which is just what we wanted

But actually we only care for the one call that produced the correct result:

19690720=try/~i.100

This gives us a truth table: a table full of zeroes and a single one. All we need to do is unravel the matrix and find the index of that single true value and we’re done.

The reason that we’re done is that the solution is “What is 100 noun + verb?” Not quite surprisingly, as 100 is the length of a row, that’s exactly what we’re getting.

I.,19690720=try/~i.100
  1. , unravels the matrix
  2. I. find the index and defaults to finding the value 1

Tags:

Add Comment

2019-12-07 J Programming Language

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:

  • # w is the tally of words, resulting in 4
  • % 2 divides it by two, resulting in 2
  • i. creates a list of numbers up to that, resulting in 0 1
  • 2 * multiplies each number by two, resulting in 0 2
  • i { w picks each element of w by the index given in i, resulting in the following:
┌──┬────┐
│14│1969│
└──┴────┘
  • ". > unboxes the array (resulting in a list of strings) and evaluates each element, resulting in a list of numbers: 14 1969

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
  • (# w) is the tally of words, resulting in 4
  • 4 $ 1 0 creates an array of four elements, filling it with 1 and 0, resulting in 1 0 1 0
  • 1 0 1 0 # w picks the first and third element, resulting in the following:
┌──┬────┐
│14│1969│
└──┴────┘

And we’re back to where we were before. Slightly more elegant, I guess?

Anyway, the code to solve the first problem of day 1:

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.

Now that I’ve written a toy program in J I wonder how anybody can write complex applications in it. Perhaps it could work. It still baffles me. To understand my inability to express my muteness in the language you have to understand that I was looking at table of contents of reference books and didn’t understand what I was looking at, didn’t know where to get the answers to my questions (such as how to remove every second cell of an array).

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!

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

Tags:

Comments on 2019-12-07 J Programming Language

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

Add Comment

Comments


Please make sure you contribute only your own work, or work licensed under the GNU Free Documentation License. Note: in order to facilitate peer review and fight vandalism, we will store your IP number for a number of days. See Privacy Policy for more information. See Info for text formatting rules. You can edit the comment page if you need to fix typos. You can subscribe to new comments by email without leaving a comment.

To save this page you must answer this question:

Just say HELLO