Pages

2022-02-17

An APL Translation of 'Square Joy: Trapped Rainwater' J Posting

 I saw an interesting post entitled "Square Joy: Trapped Rainwater" on the mmapped blog, describing how to approach the problem of computing water levels in a 2D Flatland cityscape.

The configuration of bars with heights 0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1, and the water trapped by this configuration.



The author uses J, so I thought I'd take a quick look at converting the basic computation into its parent language, APL. Turns out it's quite simple (minus the graphic representation which I haven't yet looked at doing in GNU APL).

J has the  /\. adverb ('suffix'), here applied to the 'insert' (/) which it shares with APL. APL must simulate it via two applications of the reversal (⌽) function:

      H ← 0 1 0 2 1 0 1 3 2 1 2 1
      ⌈\H
0 1 1 2 2 2 2 3 3 3 3 3
      ⌽⌈\⌽H    ⍝ Note the use of 'reversal' here (⌽) with insert (/) to match J's 'suffix scan'
3 3 3 3 3 3 3 3 2 2 2 1
      (⌈\H) ⌊ (⌽⌈\⌽H)
0 1 1 2 2 2 2 3 2 2 2 1


Maybe later I'll try to create the visual renderings in GNU APL to match what the J solution does.

2022-01-13

String Interpolation in APL -- Formatted string output ala printf()

This is not as sophisticated as C's printf() of course, but it's enough for many uses.


  ∇r ← s sInterp sv
⍝⍝ Interpolate items in sv into s (string field substitution)
⍝ s: string - format string, '∆' used for interpolation points
⍝ sv: vector - vector of items to interpolate into s
⍝ r: interpolated string
  s[('∆'=s)/⍳⍴s] ← ⊃¨(⍕¨sv)
  r ← ∊s
∇
      'Mary had a ∆ lamb, its fleece was ∆ as ∆.' sInterp 'little' 'black' 'night'
Mary had a little lamb, its fleece was black as night.
      'Mary had a ∆ lamb, its fleece was ∆ as ∆.' sInterp 'little' 'large' 42
Mary had a little lamb, its fleece was large as 42.


2022-01-07

A Recursive Depth-First Maze Generator in GNU APL

Rosettacode.org is a great place to grab little ideas and apply them to learning a new language; especially if there isn't a solution there yet in the language you're learning! This past week I took some spare time in the evenings to implement the classic maze generator problem, in GNU APL.

Takeaways:

  • Sometimes just manipulating the string representation of a maze is easier than trying to come up with a clever binary representation (I started with the idea that each maze cell's walls could be a 4-bit field, eg. nsew = [3210], but the shared walls between cells made it too messy);
  • GNU APL's ? (shuffle) operator appears to have a static seed and the docs don't clearly state how to seed it: the ⎕RL system variable has the shuffle op's internal state so assigning to it will seed the PRNG, eg:

⎕RL ← +/ ⎕TS ⍝⍝ Seed ⎕RL (?) PRNG with sum of timestamp Y, M, D, H, M, S, ms


#!/usr/local/bin/apl --script --
 ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝
⍝                                                                    ⍝
⍝ mazeGen.apl                          2022-01-07  19:47:35 (GMT-8)  ⍝
⍝                                                                    ⍝
 ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

∇initPRNG
  ⍝⍝ Seed the internal PRNG used by APL ? operator
  ⎕RL ← +/ ⎕TS   ⍝⍝ Not great... but good enough
∇

∇offs ← cellTo dir
  ⍝⍝ Return the offset (row col) to cell which lies in compass (dir)
  offs ← ∊((¯1 0)(0 1)(1 0)(0 ¯1))[('nesw'⍳dir)]
∇

∇doMaze rc
  ⍝⍝ Main function
  0 0 mazeGen rc      ⍝⍝ Do the maze gen
  m                   ⍝⍝ output result
∇

∇b ← m isVisited coord;mr;mc
  →( ∨/ (coord[1] < 1) (coord[2] < 1) )/yes
  →( ∨/ (coord > ⌊(⍴m)÷2) )/yes
  b ← ' ' ∊ m[2×coord[1];2×coord[2]]
  →0
yes:
  b←1
∇

∇c mazeGen sz ;dirs;c;dir;cell;next
  →(c≠(0 0))/gen
init:
  c ← ?sz[1],?sz[2]
  m ← mazeInit sz
gen:
  cell ← c
  dirs ← 'nesw'[4?4]
  m[2×c[1];2×c[2]] ← ' '  ⍝ mark cell as visited
dir1:
  dir ← dirs[1]
  next ← cell + cellTo dir
  →(m isVisited next)/dir2
  m ← m openWall cell dir
  next mazeGen sz
dir2:
  dir ← dirs[2]
  next ← cell + cellTo dir
  →(m isVisited next)/dir3
  m ← m openWall cell dir
  next mazeGen sz
dir3:
  dir ← dirs[3]
  next ← cell + cellTo dir
  →(m isVisited next)/dir4
  m ← m openWall cell dir
  next mazeGen sz
dir4:
  dir ← dirs[4]
  next ← cell + cellTo dir
  →(m isVisited next)/done
  m ← m openWall cell dir
  next mazeGen sz
done:
∇

∇m ← mazeInit sz;rows;cols;r
  ⍝⍝ Init an ASCII grid of size (rows cols) which
  ⍝⍝ has all closed and unvisited cells:
  ⍝⍝
  ⍝⍝  +-+
  ⍝⍝  |.|
  ⍝⍝  +-+
  ⍝⍝
  ⍝⍝ @param sz - tuple (rows cols)
  ⍝⍝ @return m - a (rows × cols) ASCII matrix
  ⍝⍝⍝⍝

  initPRNG
  (rows cols) ← sz
  r ← ∊ (cols ⍴ ⊂"+-" ),"+"
  r ← r,∊ (cols ⍴ ⊂"|." ),"|"
  r ← (rows,(⍴r))⍴r
  r ← ((2×rows),(1+2×cols))⍴r
  r ← r⍪ (∊ (cols ⍴ ⊂"+-" ),"+")
  m ← r
∇

∇r ← m openWall cellAndDir ;ri;ci;rw;cw;row;col;dir
  (row col dir) ← ∊cellAndDir
  ri ← 2×row
  ci ← 2×col
  (rw cw) ← (ri ci) + cellTo dir
  m[rw;cw] ← ' '   ⍝ open wall in (dir)
  r ← m
∇

⎕IO←1

doMaze 9 9
)OFF

russtopia@rlm-devuan:~/GNUAPL$ workspaces/mazeGen.apl 
+-+-+-+-+-+-+-+-+-+
|     |       |   |
+-+ + +-+-+ + +-+ +
|   |       |   | |
+ +-+-+-+-+-+-+ + +
|     |     | | | |
+-+-+ +-+ + + + + +
|   |   | |   |   |
+ + +-+-+ +-+-+-+ +
| | |   |       | |
+ +-+ + + +-+ +-+ +
| |   |   |   |   |
+ + +-+-+-+ + + +-+
| |   |   | | | | |
+ +-+ + +-+ +-+ + +
|   | | |   |   | |
+ +-+ + + +-+ +-+ +
|       |         |
+-+-+-+-+-+-+-+-+-+

2021-11-28

Parsing script arguments in GNU APL (⎕ARG)

In scripting languages one often wants to run the overall script like a standalone program, with a top 'main' function, yet also allow the script to be read into the REPL for editing, testing interactively and so on.

In Python a common way to do this is by checking the __main__ variable to determine execution scope.

GNU APL has the --script switch to enable invoking a .APL file and immediately executing it. In standard UNIX fashion the -- switch is also useful to divide GNU APL arguments (those for the interpreter itself) from those intended for the script. Using these two bits of information one can do the same thing in APL:


#!/usr/bin/env apl --script --

∇scriptFunction
    ⍝⍝ Whatever the script should do by default when run from shell
    ⎕←'This is an APL script'



⍝ Parse args & run 'main' if not in REPL mode
⍝ in REPL mode, ⎕ARG[1] is the script path itself
⍝ (Recalling that by default, ⎕IO = 1)
∇checkMode;args
    args←(⎕ARG⍳⊂"--")↓⎕ARG ⍝ Drop all args up to and including "--"
    →(~(⊂'--script')∊⎕ARG)/0 ⍝ If "--script" not present, quit to allow REPL
    ⍝⍝ ⎕←"[",args[1],"]" ⍝ (just debugging to see args)
    scriptFunction ⍎∊args[2] ⍝ in --script mode, so run default func


checkMode ⍝⍝ check the ⎕ARG vector for --script mode

2021-09-12

A Hexadecimal Pronunciation Guide, by Robert A. Magnuson - Datamation Vol. 14, No. 1, Jan 1968


I recall discussing with a fellow CS student many years ago the topic of how speaking hexadecimal values seemed to be.. well, clunky. We both thought it strange no one had standardized a set of word stems like we have for the base ten numerals, 'teens', 'fortieth', and so on. All these years later I discovered, as it turns out, someone did. It just didn't catch on, for some reason.

An obscure article in Datamation, Vol. 14, No. 1 dated January, 1968 (alternate link, archive.org) contains an article by one Robert A. Magnuson, proposing an extension to English number pronunciation for hexadecimal:


Table 2. New Names for Hexadecimal Digits
A ann
B bet
C chris
D dot
E ernest
F frost
The pronunciation of the -teen's and -ty's for the new
digits is shown in Table 3. Note that the analog of the decimal pronunciation system has been used. The new name for each new digit has been chosen so that at least one of the -teen and -ty modifications is familiar sounding.

Table 3. -Teen and -Ty Pronunciation for the New Digits

1 A annteen
1 B betteen
1 C christeen
1 D dotteen
1 E ernesteen
1 F frosteen
A0 annty
B0 betty
C0 christy
D0 dotty
E0 ernesty
F0 frosty

Now 29's successor 2A can be pronounced "twenty-
ann" without the slightest tendency to confuse it with 28.
The pronunciation of C4 is "christy-four," and that of A4
is "annty-four." There is no problem in distinguishing
between 1A, "annteen" and 18, "eighteen." And 88, 8A,
A8, and AA are easily distinguished when pronounced
"eighty-eight," "eighty-ann," "annty-eight," and "annty-
ann."

Some two-digit hex numbers, each representing one byte, appear with their new pronunciations in Table 4.

Table 4. One-Byte Strings with Pronunciation
2F twenty-frost
F2 frosty-two
5B fifty-bet
3E thirty-ernest
AF annty-frost

Some four-digit hex numbers, each representing two
bytes, appear with their new pronunciations in Table 5.

Table 5. Two-Byte Strings with Pronunciation
A01C annty christeen
1E00 ernesteen dotty
A007 annty oh-seven
DEAF dotty-ernest annty-frost
3A7D thirty·ann seventy-dot
47F0 forty-seven frosty

The problem of the values of the added digits being off
by one is now easily solved. Merely remember the "ages"
of these new-found friends. Learn that ann is 10, bet is
11, etc. without becoming confused with the fact that A
is associated with 1, B with 2, etc.

The problems of some of the hex digits being NUMERIC
and the others ALPHA on the model 29 keypunch is solved
in the following fashion. Select a particular finger of the
left hand, say, the little finger. No other finger of the left
hand is to be used. The home position of that left little
finger is on the NUMERIC key. The right hand is used for
typing 0-9 and the (numeric) comma while the left little
finger holds down the NUMERIC key. When· A - F arise,
they are typed with the left little finger-thus ensuring
that the NUMERIC key is not depressed. Return the left
finger to the home position on the NUMERIC key imme-
diately upon finishing A-F.


 

2021-08-17

APL Keyboard Sticker Set

APL Keyboard Stickers for Laptop or Desktop

APL uses a special symbol set, which presents a barrier to entry for new users. Furthermore, there don't seem to be many options to get a keyboard with APL symbols, other than expensive specialty ones (here or here), and they're for PC desktops only; so what's a laptop user to do?

I figured I'd take matters into my own hands, and produce a run of APL keyboard stickers. These match the standard APL keyboard map used by historical APL implementations as well as GNU APL and Dyalog APL. I got the shipment at end of August, 2021, and they look pretty good on my laptop. Now available on my Tindie store. Check it out!

Black vinyl keycap stickers, suitable for any standard laptop or PC desktop keyboard.

To use these you'll need to set a custom keymap. For Linux, See my post here. For Windows, see here, here and finally here.

With some trivial setup, modern systems have no issue at all supporting APL symbols. I wonder if its successor, J, would have even come to be if the APL character set wasn't seen as an impediment prior to the 2000s. As a newcomer to APL and J, comparing the two, J just seems so much harder to parse as it uses ASCII exclusively; it looks a lot more like line noise to me whereas APL encourages seeing a unique notation.





2021-03-02

Getting Started with APL: Keyboard Mapping and Using GNU APL on Android with TermUX

APL is an under-appreciated language, pioneering many concepts that have re-appeared in almost every language since. Iverson Ghosts! Boo.

A huge hurdle for new users is those funny symbols. How does one enter them on a modern keyboard?

Wonder no longer! It's not so tough, just not documented that well on the web today. APL has its own Unicode point so all the symbols are already inside your box waiting to be used. One can even use GNU APL on an Android phone or tablet with the Hacker's Keyboard (fork for APL layout, see bottom)

One would think APL enthusiasts would have keymaps with an easy setup script to run, to encourage newcomers, but I guess not since I didn't run across any... so here's my attempt to help out any other APL-curious people out there.


BONUS: Hard-to-find APL book links. These books are out of print and insanely expensive on the used market. I manually scanned in APL2 At A Glance to save it for posterity. It really is the best introductory text for APL2.

APL2 At a Glance (English)

APL2 Ein erster Einblick (APL2 At A Glance, German)

APL2 In Depth


Adding an APL Keymap to X terminals

From X (any windowed terminal, eg. Konsole, xfce4-terminal etc.)

--

My preferred setup uses CAPS LOCK as the APL key:

setxkbmap us,apl -option grp:caps_switch


Try the above interactively, then put it into your .bashrc and you're good to go. If you *really* need CAPS LOCK for some reason, ALT+CAPS works.

One might also be able to define it system-wide via an xorg.conf.d/ file such as the following snippet, but I haven't rebooted yet to test it:

Section "InputClass"
Identifier "system-keyboard"
MatchIsKeyboard "on"
Option "XkbLayout" "us,apl"
Option "XkbModel" "pc104"
#Option "XkbVariant" ",dvorak"
Option "XkbOptions" "grp:caps_switch"
EndSection

APL Keys For 'dfns' (lambdas)


Dyalog APL and GNU APL both use the ⍺ and ⍵ symbols for the unbound left- and right-hand variables of 'dfns' (basically lambda functions). These are standard for both APL dialects, but GNU APL also recognizes APL Functional Symbol alpha-underber, ⍶:U+2376, APL Functional Symbol omega-underbar, ⍹:U+2379 and lowercase Greek chi, χ:U+03C7.

On my Dell XPS 15 laptop, it seems using the CAPS key as the APL COMPOSE in combination with the SHIFT key does not work for the W/S/X key column. So I chose the U and C keys as alternates to yield omega-underbar and chi, respectively, instead, as Dyalog and GNU APL don't appear to have anything assigned to those with SHIFT. Modifications to my .Xmodmap are thus as follows:

keycode  25 = w W U2375 U2379
keycode  38 = a A U237A U2376
keycode  30 = u U U2193 U2379
keycode  53 = x X U2283 U03C7
keycode  54 = c C U2229 U03C7

.. and yet on my Acer Aspire, keys W and X work just fine. Weird.

To customize your X modmap (be sure you've already added the ,apl to your xkbmap above):
$ xmodmap -pke >~/.Xmodmap
$ vi .Xmodmap  ## editing the above
$ xmodmap ~/.Xmodmap   # To reload

Download my .Xmodmap keymap file here:

APL Linux Console Key Map


Download my console keymap file here: 

https://www.blitter.com/nextcloud/index.php/s/9oY7pEbDZeiZNfq


As root,

# loadkeys APL.kmap


Almost the same as the X mapping -- CAPS LOCK and shift+CAPS LOCK are combo keys with any other key in the GNU APL mapping for APL symbols; right-ALT is APL-mode lock.

APL fonts require running within an 'fbterm' on virtual consoles, so install that first, and if you want to use APL from the console often, start 'fbterm' and 'loadkeys' from startup.

GNU APL on Android with TermUX

* open TermUX.

* Make sure you have lots of free space on your phone, as you're going to be building GNU APL from source using g++

$ apt-get install subversion

$ apt-get install g++

## might need autotools and other things as well (automake, autoconf, etc.)

$ svn checkout svn://savannah.gnu.org/apl $ cd apl/trunk

You may have issues with network timeouts checking out the repo. If so, retry after running an svn cleanup. if you keep having trouble, fetch it on a PC Linux machine, tar+gzip the whole svn/trunk dir and then use 'scp' to just copy it to your TermUX home dir (either by installing openssh in TermUX or using an Android ssh program... but you'll have to hunt around in your phone's filesystem to find your TermUX home dir in that case...)


[Again, within TermUX]:

$ apt-get install ncurses pcre pcre2
$ cd trunk/   # wherever gnu apl source from svn was fetched
$ ./configure
$ make
$ mkdir $HOME/bin
$ cp src/apl $HOME/bin
$ export PATH=$HOME/bin:$PATH ## or set up a .bashrc or .profile with this to make it permanent


.. I might have missed a few apt-get calls required for some libraries but otherwise it did build just fine right on the phone!


Oh, and remove the svn/trunk afterwards because you'll probably be low on space :)


Finally, install the fork of Hacker's Keyboard with APL layout/language available here!