├── media └── ais_4.jpg ├── Makefile ├── Dockerfile ├── apl.sh └── README.md /media/ais_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justin2004/apl_in_the_shell/HEAD/media/ais_4.jpg -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build linux: 2 | docker build --platform linux/amd64 --build-arg=uid=`id -u` --build-arg=gid=`id -g` -t justin2004/apl_in_the_shell . 3 | 4 | # build mac: 5 | # docker build --platform linux/arm64 --build-arg=uid=`id -u` --build-arg=gid=`id -g` -t justin2004/apl_in_the_shell . 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # vim: filetype=dockerfile 2 | # FROM debian:11 3 | FROM ubuntu:22.04 4 | USER root 5 | RUN apt-get update && apt-get install -y curl libtinfo5 6 | ARG uid=1000 7 | ARG gid=1000 8 | ARG user=containeruser 9 | RUN groupadd -g $gid $user || true 10 | RUN useradd $user --uid $uid --gid $gid --home-dir /home/$user && \ 11 | mkdir /home/$user && \ 12 | chown $uid:$gid /home/$user 13 | 14 | # utf 8 support 15 | RUN apt-get install -y locales 16 | RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \ 17 | locale-gen 18 | ENV LANG en_US.UTF-8 19 | ENV LANGUAGE en_US:en 20 | ENV LC_ALL en_US.UTF-8 21 | 22 | WORKDIR /home/containeruser 23 | RUN curl -O https://www.dyalog.com/uploads/php/download.dyalog.com/download.php?file=19.0/linux_64_19.0.50027_unicode.x86_64.deb 24 | # RUN curl -O https://www.dyalog.com/uploads/php/download.dyalog.com/download.php?file=18.2/linux_64_18.2.50027_unicode.x86_64.deb 25 | # RUN dpkg -i linux_64_18.2.50027_unicode.x86_64.deb 26 | RUN dpkg -i linux_64_19.0.50027_unicode.x86_64.deb 27 | USER $user 28 | ADD apl.sh /home/containeruser 29 | USER root 30 | # NOTE patch dyalogscript (will be fixed in 19.x) 31 | RUN sed -i -e 's/-f/-r/' /usr/bin/dyalogscript 32 | RUN chmod 555 /home/containeruser/apl.sh 33 | USER $user 34 | 35 | WORKDIR /mnt 36 | 37 | -------------------------------------------------------------------------------- /apl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | RENDER="" 4 | CSVOUTPUT="" 5 | FINAL_OUTPUT="⎕←" 6 | INPUT="SET" 7 | # this imports a function that can render function trains as ASCII trees 8 | PREAMBLE="'dft' ⎕cy 'dfns'" 9 | 10 | print_usage() { 11 | echo "Usage: apl [OPTION] {FUNCTION} [input file ⍵]" 12 | echo " apl [OPTION] {FUNCTION} [input file ⍺] [input file ⍵]" 13 | echo " apl --no-input [OPTION] {EXPRESSION}" 14 | echo "" 15 | echo " -d, --debug" 16 | echo " print the generated APL expression, to stderr, before it is evaluated" 17 | echo "" 18 | echo " -ni, --no-input" 19 | echo " don't attempt to read from stdin" 20 | echo " NOTE: reading from stdin is assumed so you need this option if you just" 21 | echo " want to evaluate an expression with no other input" 22 | echo "" 23 | echo " -r {function name}, --render {function name}" 24 | echo " use function (either 'disp' or 'display') to render the final result" 25 | echo "" 26 | echo " -oc, --output-csv" 27 | echo " print the result of the evaluation as CSV" 28 | echo "" 29 | echo " -ic, --input-csv" 30 | echo " treat input file ⍵ as a CSV and read it in using ⎕CSV" 31 | echo " then ⍵ to your function is the array produced by ⎕CSV" 32 | echo "" 33 | echo " -ch, --use-csv-headers" 34 | echo " make the column names of the CSV file available as variables" 35 | echo " which evaluate to the position of the column and which can be" 36 | echo " used to index into the array (by column name)" 37 | echo "" 38 | echo "Examples:" 39 | echo "" 40 | echo " see https://github.com/justin2004/apl_in_the_shell" 41 | echo "" 42 | } 43 | 44 | while :; do 45 | case $1 in 46 | -h|--help) 47 | print_usage 48 | ;; 49 | -d|--debug) DEBUG="SET" 50 | ;; 51 | -ni|--no-input) unset INPUT 52 | ;; 53 | -r|--render) 54 | RENDER="$2" 55 | script+="'display' 'disp'⎕CY'dfns' ⋄" 56 | shift 57 | ;; 58 | -oc|--output-csv) CSVOUTPUT="SET" 59 | CSVOUTPUT_EXPRESSION="'/dev/stdout'(⎕CSV⍠'IfExists' 'Replace')⍨" 60 | FINAL_OUTPUT="" # ⎕CSV output will return the number of bytes written and we don't want 61 | # that to gunk up stdout 62 | ;; 63 | -ic|--input-csv) CSVINPUT="SET" 64 | ;; 65 | -ch|--use-csv-headers) USE_CSV_HEADERS="SET" 66 | # now try to get column names 67 | csv_headers_script+="col_names_raw ← ,1↑firstargument ⋄" 68 | csv_headers_script+="col_names← {⍵/⍨^\⍵∊(¯1∘⎕c⎕a),⎕a}¨ col_names_raw ⋄" 69 | # TODO if not col_names ≡ col_names_raw then print a warning? 70 | csv_headers_script+="{⍎¨,/¯1⌽'←',1⌽({⍵},⍕∘⍪∘⍳∘≢)⍵} col_names ⋄" 71 | csv_headers_script+="firstargument ← 1↓firstargument ⋄" 72 | ;; 73 | *) break 74 | esac 75 | shift 76 | done 77 | 78 | user_function=$1 79 | first=$2 80 | second=$3 81 | 82 | if [ -z "$user_function" ] 83 | then 84 | print_usage 85 | # echo ERROR: TODO put a helpful usage message here 86 | # echo "for now look at the README for usage (https://github.com/justin2004/apl_in_the_shell)" 87 | exit 1 88 | fi 89 | 90 | if [ ! -z "$first" ] 91 | then 92 | if [ "-" = "$first" ] 93 | then 94 | first="/dev/stdin" 95 | fi 96 | if [ ! -z $CSVINPUT ] 97 | then 98 | script+="firstargument←⎕CSV"\'$first\'" 'UTF-8' (4) ⋄" 99 | # the 4 above means: "The field is to be interpreted numeric data but invalid numeric data is tolerated. Empty fields and fields which cannot be converted to numeric values are returned instead as character data" 100 | # there are other options however: https://help.dyalog.com/18.2/Content/Language/System%20Functions/csv.htm 101 | if [ ! -z $USE_CSV_HEADERS ] 102 | then 103 | script+=$csv_headers_script 104 | fi 105 | else 106 | script+="firstargument←⊃⎕NGET"\'$first\'" 1 ⋄" 107 | fi 108 | if [ ! -z "$second" ] 109 | then 110 | if [ "-" = "$second" ] 111 | then 112 | second="/dev/stdin" 113 | fi 114 | script+="secondargument←⊃⎕NGET"\'$second\'" 1 ⋄" 115 | script+="$FINAL_OUTPUT $RENDER $CSVOUTPUT_EXPRESSION firstargument ($user_function) secondargument" 116 | else 117 | script+="$FINAL_OUTPUT $RENDER $CSVOUTPUT_EXPRESSION ($user_function) firstargument" 118 | fi 119 | else 120 | first="/dev/stdin" 121 | if [ ! -z $CSVINPUT ] 122 | then 123 | # we have csv input from stdin 124 | script+="firstargument←⎕CSV"\'$first\'" 'UTF-8' (4) ⋄" 125 | # the 4 above means: "The field is to be interpreted numeric data but invalid numeric data is tolerated. Empty fields and fields which cannot be converted to numeric values are returned instead as character data" 126 | # there are other options however: https://help.dyalog.com/18.2/Content/Language/System%20Functions/csv.htm 127 | if [ ! -z $USE_CSV_HEADERS ] 128 | then 129 | script+=$csv_headers_script 130 | fi 131 | script+="$FINAL_OUTPUT $RENDER $CSVOUTPUT_EXPRESSION ($user_function) firstargument" 132 | else 133 | # we have input from stdin but it isn't csv 134 | if [ ! -z $INPUT ] 135 | then 136 | # we have input from stdin but it isn't csv AND INPUT was specified (so look for a firstargument) 137 | script+="firstargument←⊃⎕NGET"\'$first\'" 1 ⋄" 138 | script+="$FINAL_OUTPUT $RENDER $CSVOUTPUT_EXPRESSION ($user_function) firstargument" 139 | else 140 | # we have input from stdin but it isn't csv AND INPUT was not specified (so don't look for a firstargument) 141 | USE_PREAMBLE="SET" 142 | # we only want to use the preamble if --no-input is specified 143 | script+="$FINAL_OUTPUT $RENDER $CSVOUTPUT_EXPRESSION ($user_function)" 144 | fi 145 | fi 146 | fi 147 | 148 | if [ ! -z $DEBUG ] 149 | then 150 | echo DEBUG: APL script is: >&2 151 | echo DEBUG: $script >&2 152 | fi 153 | 154 | # NOTE we are creating a shebang file to workaround the dyalogscript bug that requires 155 | # the script to be a file 156 | echo '#!/usr/bin/dyalogscript DYALOG_INITSESSION=1 DYALOG_CEF=0' > /tmp/ais.apl 157 | 158 | if [ ! -z $USE_PREAMBLE ] 159 | then 160 | echo -e $PREAMBLE >> /tmp/ais.apl 161 | fi 162 | echo $script >> /tmp/ais.apl 163 | chmod 777 /tmp/ais.apl 164 | /tmp/ais.apl 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # apl_in_the_shell 2 | 3 | 4 | APL in the Shell 5 | 6 | ## what 7 | 8 | [Dyalog APL](https://dyalog.com/) in a docker container with a wrapper script that allows you to quickly use APL expressions/functions in your shell (in pipelines, etc.) 9 | 10 | ## why 11 | 12 | [This](https://github.com/justin2004/clojure-unix/blob/master/clojure-unix.md#strangely-similar) got me thinking it would be fun to intersperse APL into my shell sessions. 13 | 14 | The standard unix shell "primitives" might look arcane at first but once you learn them they are fantastic building blocks. 15 | The same is true for the APL primitives but the APL primitives are even more primitive, as you'll see in some examples below, so you can do things that aren't convenient to do with unix shell primitives. 16 | 17 | 18 | ## how (build) 19 | 20 | Have docker, make, and bash installed. 21 | 22 | In a bash shell run: 23 | 24 | `make` 25 | 26 | ## how (use) 27 | 28 | Put this into your .bashrc or similar for zsh, etc. 29 | 30 | ```bash 31 | apl() { 32 | if [ -t 0 ]; then 33 | # stdin is a terminal, OK to use -t 34 | docker run --platform linux/amd64 --rm -it -v "$(pwd)":/mnt justin2004/apl_in_the_shell \ 35 | /home/containeruser/apl.sh "$@" 36 | else 37 | # stdin is a pipe or file, don't use -t 38 | docker run --platform linux/amd64 --rm -i -v "$(pwd)":/mnt justin2004/apl_in_the_shell \ 39 | /home/containeruser/apl.sh "$@" |tr '\r' '\n' 40 | fi 41 | } 42 | ``` 43 | 44 | Now source that file in your current shell: `source ~/.bashrc` 45 | 46 | Test to see if it is working: 47 | 48 | ```bash 49 | apl --no-input '⍳5' 50 | ``` 51 | 52 | should yield: 53 | ``` 54 | 1 2 3 4 5 55 | ``` 56 | 57 | 58 | 59 | Most often we want to read from stdin or a file so that is why we needed to specify `--no-input` above to indicate that we just want to evaluate an expression with no other input. 60 | 61 | If you don't specify `--no-input` or `-ni` then the process will wait for you to type something and press enter. 62 | 63 | ## examples 64 | 65 | ### Printing a function (train) tree: 66 | 67 | ```bash 68 | apl -ni '(+/÷≢) dft 1' 69 | ┌─┼─┐ 70 | / ÷ ≢ 71 | ┌─┘ 72 | + 73 | ``` 74 | 75 | This is useful to reference while constructing function trains as it indicates how they are parsed. 76 | 77 | (NOTE: in the current Dyalog 19.x we need to use this `dft` function but once user commands (specifically `]box`) are fixed the `dft` function won't be needed.) 78 | 79 | 80 | ### How many users are running processes? 81 | 82 | ```bash 83 | ps -e -o user= | sort -u | wc -l 84 | 13 85 | ``` 86 | 87 | ```bash 88 | ps -e -o user= | apl '≢∪' 89 | 13 90 | ps -e -o user= | apl '≢∪' - 91 | 13 92 | ps -e -o user= | apl '≢∪' /dev/stdin 93 | 13 94 | ``` 95 | 96 | Notice that reading from stdin is assumed so you don't have to specify it. 97 | 98 | --- 99 | 100 | ### Generate a sequence of integers (one per line) 101 | 102 | ```bash 103 | apl -ni '⍪⍳10' 104 | 1 105 | 2 106 | 3 107 | 4 108 | 5 109 | 6 110 | 7 111 | 8 112 | 9 113 | 10 114 | 115 | seq 10 116 | 1 117 | 2 118 | 3 119 | 4 120 | 5 121 | 6 122 | 7 123 | 8 124 | 9 125 | 10 126 | ``` 127 | 128 | --- 129 | 130 | ### Generate 10 odd numbers 131 | 132 | ```bash 133 | for (( i = 1; i < 20; i=i+2 )); do echo $i ; done 134 | 1 135 | 3 136 | 5 137 | 7 138 | 9 139 | 11 140 | 13 141 | 15 142 | 17 143 | 19 144 | 145 | apl -ni '⍪¯1+2×⍳10' 146 | 1 147 | 3 148 | 5 149 | 7 150 | 9 151 | 11 152 | 13 153 | 15 154 | 17 155 | 19 156 | ``` 157 | 158 | --- 159 | 160 | ### Histogram on process executable names and users running them 161 | 162 | ```bash 163 | ps -e -o comm,user | sort | uniq -c | sort -nr | head 164 | 69 chrome justin 165 | 38 bash justin 166 | 8 docker justin 167 | 6 vi justin 168 | 6 containerd-shim root 169 | 5 vim justin 170 | 5 ranger justin 171 | 4 java root 172 | 3 sh justin 173 | 3 kdmflush root 174 | 175 | ps -e -o comm,user | apl "{10↑v⌷⍨⊂⍒v←{⍺,⍨≢⍵}⌸⍵}" 176 | 69 chrome justin 177 | 38 bash justin 178 | 9 docker justin 179 | 6 vi justin 180 | 6 containerd-shim root 181 | 5 vim justin 182 | 5 ranger justin 183 | 4 java root 184 | 3 sh justin 185 | 3 kdmflush root 186 | ``` 187 | 188 | You can also use two of Dyalog APL's General Utility Functions for rendering results: 189 | [disp](https://dfns.dyalog.com/n_disp.htm) 190 | and 191 | [display](https://dfns.dyalog.com/n_display.htm) 192 | 193 | ### To do this specify one of the rendering functions with `-r` (r as in render): 194 | 195 | ```bash 196 | ps -e -o comm,user | apl -r disp "{5↑v⌷⍨⊂⍒v←{⍺,⍨≢⍵}⌸⍵}" 197 | ┌──┬──────────────────────┐ 198 | │41│chrome justin│ 199 | ├──┼──────────────────────┤ 200 | │34│bash justin│ 201 | ├──┼──────────────────────┤ 202 | │8 │vim justin│ 203 | ├──┼──────────────────────┤ 204 | │5 │vi justin│ 205 | ├──┼──────────────────────┤ 206 | │4 │sh justin│ 207 | └──┴──────────────────────┘ 208 | 209 | ps -e -o comm,user | apl -r display "{5↑v⌷⍨⊂⍒v←{⍺,⍨≢⍵}⌸⍵}" 210 | ┌→────────────────────────────┐ 211 | ↓ ┌→─────────────────────┐ │ 212 | │ 40 │chrome justin│ │ 213 | │ └──────────────────────┘ │ 214 | │ ┌→─────────────────────┐ │ 215 | │ 34 │bash justin│ │ 216 | │ └──────────────────────┘ │ 217 | │ ┌→─────────────────────┐ │ 218 | │ 8 │vim justin│ │ 219 | │ └──────────────────────┘ │ 220 | │ ┌→─────────────────────┐ │ 221 | │ 5 │vi justin│ │ 222 | │ └──────────────────────┘ │ 223 | │ ┌→─────────────────────┐ │ 224 | │ 4 │sh justin│ │ 225 | │ └──────────────────────┘ │ 226 | └∊────────────────────────────┘ 227 | ``` 228 | 229 | 230 | ### CSV 231 | 232 | Putting a csv file in an ASCII table using [⎕CSV](http://help.dyalog.com/latest/Content/Language/System%20Functions/csv.htm): 233 | 234 | ```bash 235 | cat a.csv 236 | name,pet,age 237 | bob,fido,33 238 | fred,sam,10 239 | jane,sal,3 240 | 241 | 242 | apl -r disp --input-csv "⊢" a.csv 243 | ┌────┬────┬───┐ 244 | │name│pet │age│ 245 | ├────┼────┼───┤ 246 | │bob │fido│33 │ 247 | ├────┼────┼───┤ 248 | │fred│sam │10 │ 249 | ├────┼────┼───┤ 250 | │jane│sal │3 │ 251 | └────┴────┴───┘ 252 | ``` 253 | 254 | The `--input-csv` (or `-ic`) option is equivalent to: 255 | 256 | ```bash 257 | apl -ni -r disp "⎕CSV 'a.csv'" 258 | ``` 259 | 260 | Note we specify `--no-input` (`-ni`) there because the csv file name is specified inside the APL expression (as a character vector) and not as one of the command line arguments. 261 | 262 | --- 263 | 264 | ### Transpose a csv file: 265 | 266 | ```bash 267 | cat a.csv | apl --output-csv -ic '⍉' 268 | name,bob,fred,jane 269 | pet,fido,sam,sal 270 | age,33,10,3 271 | 272 | cat a.csv | apl -r disp -ic '⍉' 273 | ┌────┬────┬────┬────┐ 274 | │name│bob │fred│jane│ 275 | ├────┼────┼────┼────┤ 276 | │pet │fido│sam │sal │ 277 | ├────┼────┼────┼────┤ 278 | │age │33 │10 │3 │ 279 | └────┴────┴────┴────┘ 280 | ``` 281 | 282 | Notice above that `--output-csv` (or `-oc`) prints csv to stdout. 283 | If you don't specify `--output-csv` Dyalog APL's matrix rendering would be used. 284 | 285 | --- 286 | 287 | ### Add an `id` column to a csv file: 288 | 289 | ```bash 290 | apl -oc -ic "{⍵,⍨(⊂'id'),⍳¯1+≢⍵}" a.csv 291 | id,name,pet,age 292 | 1,bob,fido,33 293 | 2,fred,sam,10 294 | 3,jane,sal,3 295 | ``` 296 | 297 | The awk solution isn't bad: 298 | 299 | ```bash 300 | awk -v OFS="," 'NR==1{print "id",$0} {print NR,$0}' a.csv 301 | id,name,pet,age 302 | 1,name,pet,age 303 | 2,bob,fido,33 304 | 3,fred,sam,10 305 | 4,jane,sal,3 306 | ``` 307 | 308 | But since awk isn't csv aware (which matters if you have quoted cells with the delimiter character in them) it can't easily handle the general case where you might want to put the id column somewhere in the middle: 309 | 310 | ```bash 311 | apl -oc -ic "{2⌽⍵,⍨(⊂'id'),⍳¯1+≢⍵}" a.csv 312 | pet,age,id,name 313 | fido,33,1,bob 314 | sam,10,2,fred 315 | sal,3,3,jane 316 | ``` 317 | 318 | Of course column ordering of csv is mostly meaningless but I've seen cases where it matters. 319 | 320 | --- 321 | 322 | ### Named indexes 323 | 324 | If you want to use the column names in csv files use the `-ch` (or `--use-csv-headers`) option: 325 | 326 | ```bash 327 | cat some.csv 328 | name,weight 329 | opal,62 330 | owen,65 331 | justin,195 332 | 333 | apl -ch -ic "{⍵[;name]}" some.csv 334 | ┌────┬────┬──────┐ 335 | │opal│owen│justin│ 336 | └────┴────┴──────┘ 337 | 338 | apl -ch -ic "{⍵[;weight]}" some.csv 339 | 62 65 195 340 | 341 | # sum of all the weights 342 | apl -ch -ic "{+/⍵[;weight]}" some.csv 343 | 322 344 | 345 | # average of the weights 346 | cat some.csv | apl -ch -ic "{(+/÷≢)⍵[;weight]}" 347 | 107.3333333 348 | ``` 349 | 350 | Note that the column names that are available are just the leading alpha characters (upper and lower case). 351 | So if you have a column name "Name on business card" the column will be simply "Name". 352 | "Name4you" will be "Name". 353 | 354 | Also note when you use this option the header row is dropped. 355 | 356 | --- 357 | 358 | Putting the /etc/passwd file in an ASCII table (inspired by [this](https://www.reddit.com/r/apljk/comments/yvmn9z/apl_in_the_shell_an_implementation/j24llo7/)): 359 | ```bash 360 | head -2 /etc/passwd 361 | root:x:0:0:root:/root:/bin/bash 362 | daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 363 | 364 | apl -r disp "{↑':'(≠⊆⊢)¨⍵}" /etc/passwd 365 | ┌─────────────┬─┬─────┬─────┬──────────────────────────────────┬─────────────────┬─────────────────┐ 366 | │root │x│0 │0 │root │/root │/bin/bash │ 367 | ├─────────────┼─┼─────┼─────┼──────────────────────────────────┼─────────────────┼─────────────────┤ 368 | │daemon │x│1 │1 │daemon │/usr/sbin │/usr/sbin/nologin│ 369 | ├─────────────┼─┼─────┼─────┼──────────────────────────────────┼─────────────────┼─────────────────┤ 370 | │bin │x│2 │2 │bin │/bin │/usr/sbin/nologin│ 371 | ├─────────────┼─┼─────┼─────┼──────────────────────────────────┼─────────────────┼─────────────────┤ 372 | ... 373 | ``` 374 | 375 | --- 376 | 377 | ### Get 10 random words from the dictionary file 378 | 379 | ```bash 380 | head -400 /usr/share/hunspell/en_US.dic | tail -5 381 | Amie/M 382 | Amiga/M 383 | Amish/M 384 | Amman/M 385 | Amoco/M 386 | 387 | cat /usr/share/hunspell/en_US.dic | apl "{↑v/¨⍨~∨\¨'/'=v←⍵[10?⍴⍵]}" 388 | Kewpie 389 | Jarlsberg 390 | Delphinus 391 | Nepal 392 | Priceline 393 | Khazar 394 | Emmy 395 | Lillie 396 | Alba 397 | Dropbox 398 | 399 | cat /usr/share/hunspell/en_US.dic | shuf | head -10 | sed -e 's,/.*,,' 400 | Okefenokee 401 | Meyers 402 | merited 403 | prude 404 | migrate 405 | occultist 406 | spotter 407 | swift 408 | murmurer 409 | adopt 410 | ``` 411 | 412 | --- 413 | 414 | Just some dyadic examples 415 | 416 | ```bash 417 | cat nums.txt 418 | 1 419 | 2 420 | 3 421 | 4 422 | 423 | echo -e 'one\ntwo\nthree\nfour' | apl "{⍺}" nums.txt - 424 | 1 2 3 4 425 | 426 | echo -e 'one\ntwo\nthree\nfour' | apl "{⍵}" nums.txt - 427 | one two three four 428 | 429 | echo -e 'one\ntwo\nthree\nfour' | apl "{⍎¨⍺}" nums.txt - 430 | 1 2 3 4 431 | 432 | echo -e 'one\ntwo\nthree\nfour' | apl "{+/⍎¨⍺}" nums.txt - 433 | 10 434 | 435 | echo -e 'one\ntwo\nthree\nfour' | apl "{⍺ ⍵}" nums.txt - 436 | 1 2 3 4 one two three four 437 | 438 | echo -e 'one\ntwo\nthree\nfour' | apl "{(⍎¨⍺)∘.⍴⍵}" nums.txt - 439 | o t t f 440 | on tw th fo 441 | one two thr fou 442 | oneo twot thre four 443 | 444 | echo -e 'one\ntwo\nthree\nfour' | apl -r disp "{(⍎¨⍺)∘.⍴⍵}" nums.txt - 445 | ┌────┬────┬────┬────┐ 446 | │o │t │t │f │ 447 | ├────┼────┼────┼────┤ 448 | │on │tw │th │fo │ 449 | ├────┼────┼────┼────┤ 450 | │one │two │thr │fou │ 451 | ├────┼────┼────┼────┤ 452 | │oneo│twot│thre│four│ 453 | └────┴────┴────┴────┘ 454 | ``` 455 | 456 | ## entering APL glyphs 457 | 458 | I use [this](https://aplwiki.com/wiki/Typing_glyphs_on_Linux#setxkbmap) approach on Linux. 459 | You run that single command mentioned there. 460 | Then you'll be able to use your keyboard's right Alt key as a modifier to enter APL characters. 461 | For instance, you enter can the iota character ⍳ by pressing the right Alt + i, the rho character ⍴ by pressing the right Alt + r and so on. 462 | 463 | Also see [here](https://aplwiki.com/wiki/Typing_glyphs_on_Linux) for other input methods. 464 | 465 | I sometimes use [this](https://github.com/phantomics/april#enabling-apl-input-in-vim) vim input approach as well. 466 | 467 | ## similar projects 468 | 469 | For q there is [awq](https://github.com/adavies42/qist/blob/master/lib/awq.q). 470 | 471 | For j there is [this](https://topaz.github.io/paste/#XQAAgAD//////////wARiEJGPfQYaqZnr3qfcB//srbYI6pNxoin1UgFBxUFegNZnW0crudtmhW/2jpcTJPZYgurbkV0/cxNLTtf4Ia2i2Tl2MLlJ0drB2SIdCgWf2N3TjHzS4X7lGSgSECr5+Z3C5uyg3avmxw1Bj+NScdyFtEB3VsC/6Zs/MhK8N8o1Ud5ZgVoo/TpVuVCPO9edQbL2zKI0IEOuISzIWAh+WuSAVqNeuYiOsAhJf8cF2A507uHqM2xwdmgQWrI5Xe1go+5wpB96yBid/Vgz5icBskwt1xaSoeg74+qxE1ROrWXPgbNoJ2/HTk+prb9b48kJT4yEymWR8KNwUm643Dq/Xd4UUaaEcz1dtAeSDCfc6w1of3/5PI=). 472 | 473 | [jacinda](https://hackage.haskell.org/package/jacinda) "APL meets AWK" 474 | 475 | 476 | ## NOTES 477 | 478 | Dyalog APL is free for non-commercial use. 479 | See [here](https://www.dyalog.com/prices-and-licences.htm) for license details. 480 | 481 | Dyalog APL is running in a docker container with your host's PWD bindmounted into the container at the container's PWD. 482 | If you reference a file name it must use relative (from your PWD) paths. 483 | So you can't reference files above your host PWD. 484 | And you can't `..` to get above your host's PWD. 485 | --------------------------------------------------------------------------------