Skip to content

Instantly share code, notes, and snippets.

@0racle
Last active July 23, 2024 07:30
Show Gist options
  • Save 0racle/91b63c16a15e16bc6ab5548a47fe1ccd to your computer and use it in GitHub Desktop.
Save 0racle/91b63c16a15e16bc6ab5548a47fe1ccd to your computer and use it in GitHub Desktop.
J Profile Definitions

Here's a rundown of definitions in my .jprofile.ijs, which get loaded in all J sessions.

Some of these are so simple they don't need names, but sometimes I just put things in here so I don't forget how to spell them.

Not all the functions are the most efficient, they're just there for playing with data. Often in a script, I will copy a function in, or re-write one that is more specific to the given problem domain.

Names

Alphanumerics

NB. AlphaNumerics             || Golf versions
azuc   =: (65 ,: 26) ];.0 a.  NB. u: 65 + i. 26
azlc   =: (97 ,: 26) ];.0 a.  NB. u: 97 + i. 26
digits =: (48 ,: 10) ];.0 a.  NB. u: 48 + i. 10
alnum  =: azuc,azlc,digits

These are useful in light string parsing, and also as test data

Functions

Visualise boolean arrays different ways

B =: 0&$: : {{
    B0 =. ((u: 183 9619) { ~ ])"2
    B1 =. (u: 16b20 16b2584 16b2580 16b2588) {~ (,:~2 1) #.@,;._3 ]
    B2 =. (,:~4 2) (10240 u:@+ 40283 #.@A. ,);._3 ]
    B3 =. ((u: 11036 11035) { ~ ])"2
    B0`B1`B2`B3@.x y
}}

I originally had just B defined like B0, but I then I nabbed the others from here.

Example usage

   B (<:/&i. -) 5
▓▓▓▓▓
▓▓▓▓·
▓▓▓··
▓▓···
▓····
   1 B (<:/&i. -) 10
█████████▀
███████▀
█████▀
███▀
█▀
   2 B (<:/&i. -) 20
⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋
⣿⣿⣿⣿⣿⣿⡿⠋⠀⠀
⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀
⣿⣿⡿⠋⠀⠀⠀⠀⠀⠀
⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀

Box and laminate arrays for comparison

Over =: (8!:1)@,:&(<"_1)

On =: {{
    x ((,:&(<"_1))`((,~ <"_1)~)@.(32 = (3!:0) {. y)) y
}}

Over is a convenience function for comparing 2 arrays that may be of different types (hence the boxing).

Similar to the ,[0.5] thingy in APL.

   (Over azlc&i.) 'alphabetical'
┌─┬──┬──┬─┬─┬─┬─┬──┬─┬─┬─┬──┐
│a│l │p │h│a│b│e│t │i│c│a│l │
│0111570141982011│
└─┴──┴──┴─┴─┴─┴─┴──┴─┴─┴─┴──┘
   (Over B@e.&'aeiou') 'iversonian'
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│i│v│e│r│s│o│n│i│a│n│
│▓│·│▓│·│·│▓│·│▓│▓│·│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘

On is a more heavy-weight version that doesn't format the output, and can stack multiple lists

   (i. 4) On 'RULD' On (, -) =/~ i. 2
┌───┬───┬────┬────┐
│0123   │
├───┼───┼────┼────┤
│R  │U  │L   │D   │
├───┼───┼────┼────┤
│1 00 1_1 00 _1│
└───┴───┴────┴────┘

I use Over more, so I should probably swap the names around 🤷

Extract numeric values from string

For ℕatural numbers (well... Whole numbers, cos 0, but I like the name Nats)

Nats =: {{ ". ('1234567890 ') {~ '1234567890' i. y }}

Or Integerℤ (again... I just perfer the name Nums)

Nums =: '1234567890_ ' ".@:{~ '1234567890-' i. ]

Pass a string with something separating the numbers

   */ Nats '175m × 50m'
8750
   Nums '12 + -3 == 9'
12 _3 9

I guess I should add '.' and make a Rats verb, but it hasn't come up.

Partition adverb similar to APL's

P =: {{ (1, 2 </\ x) u;.1&((0 < x)&#) y }}

This closely mimics the APL operator, and - among other uses - can also be used to extract numbers from a string.

   digits (e.~ <P ]) '175m × 50m'
┌───┬──┐
│17550│
└───┴──┘
   */ digits (e.~ ".P ]) '175m × 50m'
8750

See this gist for more examples.

Extended E.

Credit to @tubular on The APL Discord for this

E =: +./@(-@(#: [: i. */)@$@[ |. E.)

Like E. but the resulting mask include the whole match (not just the upper left corner), with possible overlaps

   < B ('ab',:'ba') E 'ab' {~ ? 10 20 $ 2
┌────────────────────┐
│····················│
│········▓▓··········│
│········▓▓··········│
│··············▓▓····│
│▓▓···▓▓·····▓▓▓▓····│
│▓▓··▓▓▓·····▓▓▓▓▓···│
│····▓▓·······▓▓▓▓···│
│·········▓▓·▓▓······│
│·········▓▓▓▓▓······│
│···········▓▓·······│
└────────────────────┘

Extract runs from a sequence

Runs =: {{ y u;.1~ 1, 2 ~:/\ v y }}

Conjunction for processing "runs" of values.

  • v defines how to identify runs,
  • u defines what to do with those runs
   <Runs] 'aabcccaaaaabbbc'
┌──┬─┬───┬─────┬───┬─┐
│aa│b│ccc│aaaaa│bbb│c│
└──┴─┴───┴─────┴───┴─┘
   #Runs] 'aabcccaaaaabbbc'
2 1 3 5 3 1
   <Runs('a'=]) 'aabcccaaaaabbbc'
┌──┬────┬─────┬────┐
│aa│bccc│aaaaa│bbbc│
└──┴────┴─────┴────┘

If you don't care about false runs, use P

   (('a'=]) <P ]) 'aabcccaaaaabbbc'
┌──┬─────┐
│aa│aaaaa│
└──┴─────┘

Modify values in an array with a function

Adjust =: (@:{)`[`]}    NB. {{ (u x { y) x} y }}

Alter =: {{ x } y ,: u y }}

Adjust is Like Amend } but takes a function to modify selected items

   2 3 (*&10) Adjust i.  6
0 1 20 30 4 5
   ] a =. 1 (< ;~ i. 3)} 5 5 $ 0
1 1 1 0 0
1 1 1 0 0
1 1 1 0 0
0 0 0 0 0
0 0 0 0 0
   (<1 2 3;1 2 3) (1 XOR ]) Adjust a
1 1 1 0 0
1 0 0 1 0
1 0 0 1 0
0 1 1 1 0
0 0 0 0 0

Alter is a similar function which takes a mask instead of indices

This is kinda like BQN's "Under Select" {𝔽⌾(𝕨⊸/)𝕩}

   0 1 1 0 0 toupper Alter 'alter'
aLTer
   (];.0 a) (1 XOR ]) Alter a
1 1 1 0 0
1 1 1 0 0
1 1 0 1 1
0 0 1 1 1
0 0 1 1 1

A minor concern you might have is that Alter performs u to the whole of y, which may be wasteful.

Use can modify the fork ($ #: I.@,) (which converts a boolean matrix to indices) to use a mask with Adjust

   (($ <@#: I.@,) (];.0 a)) (1 XOR ]) Adjust a
1 1 1 0 0
1 1 1 0 0
1 1 0 1 1
0 0 1 1 1
0 0 1 1 1

Amend. Adjust. Alter. Yes, I used a thesaurus.

Insert values (splice)

Insert =: {{ m ({. , x , }.) y }}

Inserts x in y, at position m

   'Three' 6 Insert 'OneTwoFour'
OneTwoThreeFour

Shuffle array

Shuffle =: $ $ ({~ ?~@#)@,

Randomises position of all items in a matrix across ranks.

Use it with Rank " to limit it's effect

   <"_1 Shuffle i. 2 2 3
┌───────┬─────┐
│5 10 117 8 1│
│0  4  96 3 2│
└───────┴─────┘
   <"_1 Shuffle"1 i. 2 2 3
┌─────┬───────┐
│1 0 26 7  8│
│3 5 411 9 10│
└─────┴───────┘
   <"_1 Shuffle"_1 i. 2 2 3
┌─────┬───────┐
│3 5 411 8  9│
│1 0 26 7 10│
└─────┴───────┘

Indices inverse

Iinv =: e.~ i.@>:@{:

Simple Indices inverse (only works with sorted lists)

NB. I.^:_1 (or I.inv) is now availble in the latest (J9.6) engine, but leaving it here in case I need it on the Playground or something.

   ] p =. /:~ 10 ? 20
1 5 6 7 8 9 10 11 15 17
   Iinv p
0 1 0 0 0 1 1 1 1 1 1 1 0 0 0 1 0 1
   I. Iinv p
1 5 6 7 8 9 10 11 15 17

Consecutive integers from x to y (inclusive)

To =: ([ + i.@(>:@-~))`(|.@$:~)@.>

Return a [closed, closed] range from x to y, either ascending or descending.

   _3 To 11
_3 _2 _1 0 1 2 3 4 5 6 7 8 9 10 11
   11 To _3
11 10 9 8 7 6 5 4 3 2 1 0 _1 _2 _3

If I want [closed, open), I can always just }:

Three-way comparison

Cmp =: {.@(/: - \:)@,&<

Get single "order" - like < or > - but on Array

   1 2 3 Cmp 1 3 2
_1
   'alpha' Cmp 'beta'
_1
   'gamma' Cmp 'beta'
1

Odometer monad

Odm =: #: i.@(*/)

Like K's !

   |: Odm 3 3
0 0 0 1 1 1 2 2 2
0 1 2 0 1 2 0 1 2

Iota monad

Iota =: #: i.

Like APL's or BQN's , given multiple values

   <"1 Iota 3 3
┌───┬───┬───┐
│0 00 10 2│
├───┼───┼───┤
│1 01 11 2│
├───┼───┼───┤
│2 02 12 2│
└───┴───┴───┘

Aliases

Some shorter (possibly inverse-able) versions of existing functions.

Ord =: (u:^:_1) :. (u:)
Chr =: (u:) :. (u:^:_1)

Uc  =: toupper :. tolower
Lc  =: tolower :. toupper

NB. Left-shift and Right-shift
Ls  =: (1&$:) : (|.!.'')
Rs  =: Ls&.|.

Time & Space

ts =: {{
    echo < dtbs,/ 'timespacex × ',('c' (8!:2) x)
    result =. x&{{ y ; (8!:0) (x, 1) * x timespacex y }}@> cutopen y
    echo ,. ('expr';'time';'space') , result
}}

A subjectively prettier timespacex

   1e6 ts '> , { ;~ i: 1';',/ ,"0/~ i: 1'
┌──────────────────────┐
│timespacex × 1,000,000│
└──────────────────────┘
┌─────────────┬────────┬─────┐
│expr         │time    │space│
├─────────────┼────────┼─────┤
│> , { ;~ i: 11.8347013520 │
├─────────────┼────────┼─────┤
│,/ ,"0/~ i: 11.3897052880 │
└─────────────┴────────┴─────┘

File / Path stuff

curfile =: {{ _1 {:: (4!:3) $ 0 }}
curpath =: ] ,~ 0 {:: fpathname@curfile
chdir   =: (1!:44)

The cur* verbs are probably not 100% reliable but works for my use case. Probably named poorly, because it's not cwd. Maybe should be scriptpath, but whatever.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment