APL Notes: CC1

Code Comparison

Zig

const std = @import("std");
test {
    var str = "The C Programming Language".*;
    str[4] += 23;
    std.debug.print("{s}\n", .{str});
}

BQN

   str ← "The C Programming Language"
   str ↩ str + -'C' = str
"The B Programming Language"

   ∾"BQN"⌾(4⊸⊑) str
"The BQN Programming Language"

J

#! /usr/bin/env ijconsole
str =: 'The C Programming Language'
str =: (a. {~ 7 + a. i. 4 { str) 4 } str
echo str
exit 0

APL

      str ← 'The C Programming Language'
      str[5] ← ⎕UCS ¯2+⎕UCS str[5]
      str
The A Programming Language

Lil

 str:"The C Programming Language"
"The C Programming Language"

 # representation math with byte array
 a:array[count str "u8"].cat[str]
<array>
 a[4]:a[4]+9
 "" fuse a.slice[0 "char"] @ range count str
"The L Programming Language"

 str[4]:"Lil"  # mutates
"The Lil Programming Language"

Zig dereferences an array in static memory to get a mutable copy, increments 'C' to change the language, and the prints the string. The J is a complete script that changes 'C' to 'J' instead. The APL is from an interactive session, and goes with 'A' for a comparable example.

The Zig is typical of static non-APL languages in that there's a bit going on in the code that's unrelated to the task. But, still, it's a short and straightforward example. The APL I think looks pretty nice, especially compared to the J. The index syntax has its faults but they don't show up here. The J is... well, at 4 { str it fetches 'C' from the string, then 7 + a. i. finds the index of 'C' in the ASCII table and then adds 7 to the index, and then a. {~ looks up that index in the ASCII table, and finally 4 } str puts 'J' into the string, and with str =: and without aliases, this is a proper mutation. Geez. I don't think it's that bad, but it certainly doesn't compare well. I haven't tried to refine the J any, but the Zig and especially the APL also haven't had any special effort; these are all first attempts.

J rewrites

Anyway, let's give J some additional attempts.

Defining some ⎕UCS like words:

   fc =: a. i. ]   NB. from char
   tc =: a. {~ ]   NB. to char
   str =: 'The C Programming Language'
   str =: str 4 }~ tc 7 + fc 4 { str
   str
The J Programming Language

Defining a 'mutate this element with this verb' adverb:

   mut =: 1 : 'y x }~ tc u fc x { y'
   str =: 'The C Programming Language'
   ]str =: 4 (7&+) mut str
The J Programming Language

Maybe just try more suggestive formatting?

   str =: 'The C Programming Language'
   ]str =: str 4}~ a.{~ 7 + a.i. 4{str
The J Programming Language

Or even:

   str =: 'The C Programming Language'
   ]str =: 4}&str a.{~ 7 + a.i. 4{str
The J Programming Language

I think that last one's the best, emphasizing idioms a.i. to convert a char to a number, a.{~ to do the reverse, etc.

You could say that it's unfortunate that it took some rewrites to get good code, but that'll improve as skill with J improves.

How about Unicode?

Zig

The original version of this page used D instead of Zig, which compared much more favorably to the J. Zig anyway is extremely distracted by memory management here:

test {
    var str: std.BoundedArray(u8, 64) = try .init(0);
    var buf: [4]u8 = undefined;
    var it: std.unicode.Utf8Iterator = .{ .bytes = "这种C编程语言", .i = 0 };
    var i: usize = 0;
    while (it.nextCodepoint()) |u| : (i += 1) {
        if (i == 2) {
            try str.append('Z');
        } else {
            const n = try std.unicode.utf8Encode(u, &buf);
            try str.appendSlice(buf[0..n]);
        }
    }
    std.debug.print("{s}\n", .{str.slice()});
}

BQN

BQN strings are lists of codepoints, so there's no difference at all:

   str ← "这种C编程语言"
   str + -'C' = str
"这种B编程语言"

   ∾"BQN"⌾(2⊸⊑) str
"这种BQN编程语言"

J

#! /usr/bin/env ijconsole
str =: ucp '这种C编程语言'
str =: utf8 2}&str a.{~ 7 + a.i. 2{str
echo str
exit 0

J's working with reference to a. ("Alphabet"), a 256-length array of ASCII characters. This is code with some suggestivity: you could do the same with reference to some other array, say for cryptographic purposes.