├── docs
├── CNAME
├── img
│ ├── lb.png
│ ├── 3DAxes.png
│ ├── Grille.png
│ ├── mathjax.png
│ ├── CubedCubes.png
│ ├── favicon-32.png
│ ├── SquaredCubes.png
│ ├── OddSquaredCubes.png
│ ├── RIDE_Trace_Window.png
│ ├── rectangle.svg
│ ├── line.svg
│ ├── point.svg
│ ├── dyalog-white.svg
│ └── numerical-integration.svg
├── assets
│ └── apl385.ttf
├── Keyboard Mnemonics.pdf
├── javascripts
│ └── config.js
├── solutions.md
├── problem-ideas.md
├── index.md
├── test.md
├── style
│ └── main.css
├── examples.md
├── Reading.md
├── Help.md
├── Assignment.md
├── Interfaces.md
├── todo.md
├── broken-keyboard-problems.md
├── dfns-and-assignment.md
├── Quad names.md
├── Workspaces.md
├── Code.md
├── Quirks.md
├── finding-and-replacing-values.md
├── Namespaces.md
├── Interpreter-internals.md
├── loops-and-recursion.md
├── basic-syntax-and-arithmetic.md
├── selecting-from-arrays.md
└── user-defined-functions.md
├── CI
├── requirements.txt
└── check_version.sh
├── .gitignore
├── package.json
├── .github
└── workflows
│ └── mkdocs-gh-pages.yml
├── README.md
├── CONTRIBUTING.md
└── mkdocs.yml
/docs/CNAME:
--------------------------------------------------------------------------------
1 | course.dyalog.com
--------------------------------------------------------------------------------
/CI/requirements.txt:
--------------------------------------------------------------------------------
1 | mkdocs==1.6.1
2 | mkdocs-material==9.6.18
3 |
--------------------------------------------------------------------------------
/docs/img/lb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/img/lb.png
--------------------------------------------------------------------------------
/docs/img/3DAxes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/img/3DAxes.png
--------------------------------------------------------------------------------
/docs/img/Grille.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/img/Grille.png
--------------------------------------------------------------------------------
/docs/assets/apl385.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/assets/apl385.ttf
--------------------------------------------------------------------------------
/docs/img/mathjax.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/img/mathjax.png
--------------------------------------------------------------------------------
/docs/img/CubedCubes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/img/CubedCubes.png
--------------------------------------------------------------------------------
/docs/img/favicon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/img/favicon-32.png
--------------------------------------------------------------------------------
/docs/Keyboard Mnemonics.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/Keyboard Mnemonics.pdf
--------------------------------------------------------------------------------
/docs/img/SquaredCubes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/img/SquaredCubes.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | site/
2 | .cache/
3 | node_modules/
4 | docs/javascripts/mathjax/
5 | docs/javascripts/@mathjax/
6 |
--------------------------------------------------------------------------------
/docs/img/OddSquaredCubes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/img/OddSquaredCubes.png
--------------------------------------------------------------------------------
/docs/img/RIDE_Trace_Window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dyalog/APLCourse/master/docs/img/RIDE_Trace_Window.png
--------------------------------------------------------------------------------
/docs/javascripts/config.js:
--------------------------------------------------------------------------------
1 | window.MathJax = {
2 | tex: {
3 | inlineMath: [["\\(", "\\)"]],
4 | displayMath: [["\\[", "\\]"]],
5 | processEscapes: true,
6 | processEnvironments: true
7 | },
8 | options: {
9 | ignoreHtmlClass: ".*|",
10 | processHtmlClass: "arithmatex"
11 | },
12 | output: {
13 | fontPath: '/javascripts/@mathjax/%%FONT%%-font'
14 | }
15 | };
16 | document$.subscribe(() => {
17 | MathJax.typesetPromise()
18 | })
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "mathjax": "^4.0.0"
4 | },
5 | "scripts": {
6 | "postinstall": "run-script-os",
7 | "postinstall:linux:macos": "mv node_modules/mathjax docs/javascripts/; mv node_modules/@mathjax docs/javascripts/",
8 | "postinstall:windows": "move node_modules\\mathjax docs\\javascripts\\ & move node_modules\\@mathjax docs\\javascripts\\"
9 | },
10 | "devDependencies": {
11 | "run-script-os": "^1.1.6"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.github/workflows/mkdocs-gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: Publish docs via GitHub Pages
2 | on:
3 | push:
4 | branches: [master]
5 | workflow_dispatch:
6 |
7 | permissions: write-all
8 |
9 | jobs:
10 | build:
11 | name: Deploy docs
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout main
15 | uses: actions/checkout@v2
16 |
17 | - name: Install dependencies
18 | uses: actions/setup-node@v5
19 | - run: npm install
20 |
21 |
22 | - name: Deploy docs
23 | uses: mhausenblas/mkdocs-deploy-gh-pages@master
24 | # Or use mhausenblas/mkdocs-deploy-gh-pages@nomaterial to build without the mkdocs-material theme
25 | env:
26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27 | CUSTOM_DOMAIN: course.dyalog.com
28 | REQUIREMENTS: CI/requirements.txt
29 |
--------------------------------------------------------------------------------
/docs/solutions.md:
--------------------------------------------------------------------------------
1 | ## FindWord
2 | The [FindWord problem](./selecting-from-arrays.md#problem-set-7)
3 |
4 | An outer product or reshape can be used for the comparison, but we need to make sure our character vector has the right shape.
5 | ```APL
6 | FindWord ← {∧/∨/⍺∘.=⍵↑⍨2⌷⍴⍺}
7 | ```
8 |
9 | This outer product generates a 3-dimensional array. It is more efficient to reshape the vector to match the matrix:
10 |
11 | ```APL
12 | FindWord ← {∧/⍺=(⍴⍺)⍴⍵↑⍨2⌷⍴⍺}
13 | ```
14 |
15 | We can compare one row with several using [the rank operator](./cells-and-axes.md#the-rank-operator).
16 |
17 | ```APL
18 | FindWord ← {∧/⍺(=⍤1)⍵↑⍨(⍴⍺)[2]}
19 | ```
20 |
21 | The comparison followed by a reduction is also expressed neatly using [the inner product operator](./Operators.md#the-inner-product).
22 |
23 | ```APL
24 | FindWord ← {⍺∧.=⍵↑⍨2⌷⍴⍺}
25 | ```
26 |
--------------------------------------------------------------------------------
/docs/problem-ideas.md:
--------------------------------------------------------------------------------
1 | # Problem ideas
2 | ## cells and axes
3 | ### car wash
4 | - rates times times? then check the lowest in each matrix
5 |
6 | We want to choose which to use of 3 competing car wash services. They are all the same distance from our house, so the only thing we will use to choose from is price. We will choose to use the service with the lowest price.
7 |
8 | Sometimes lots of people want to get their cars washed at the same time, and some times not very many people do. This means that the demand changes throughout the day and the week. To try and balance the demand, the services vary their pricing throughout the day to incentivise people to try and go at different times.
9 |
10 | The services are open from 08:30 until 18:30, 7 days a week. The rates for each car wash, 1 2 3 (TODO: snazzy names) are given as a 3-row matrix.
11 |
12 | `⊢rates ← .99+?3 12⍴20` TODO should very sensibly?
13 |
14 | Want to end up with a 3D array from which to return a single number, or perhaps a vector or matrix. We want to determine, for each day of the week and each time of day, which car wash 1 2 or 3 should we choose to go to.
15 |
16 | We start with 3 vectors and must use outer products to generate the 3D array, then reduce?
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # APL Course
2 | A self-study introduction to Dyalog APL with exercises. This course consists of a series of short instructional notes interleaved with problem sets.
3 |
4 | ## Getting Started
5 | If you're just starting out, you can use the [TryAPL](https://tryapl.org) online interpreter. Put TryAPL on one half of your screen, and view the course on the other half.
6 |
7 | Chapters and exercises involving the use of external files and some [system functions](./Quad%20names.md) require the full Dyalog system, which can be [downloaded for free from the Dyalog website](https://www.dyalog.com/download-zone.htm).
8 |
9 | If you need help typing APL glyphs (e.g. `×⌿⍳`) then see [the APL Wiki](https://aplwiki.com/wiki/Typing_glyphs#Prefix_key).
10 |
11 | ## Audience
12 | This course assumes high-school / secondary level mathematics knowledge, and some familiarity with basic programming terminology (e.g. *function*, *variable*, *recursion*). It is not intended as a general introduction to programming, but more of a fast-track to getting up and running with modern APL.
13 |
14 | ## Feedback
15 | If you have any suggestions, criticisms or praise, please [create an issue on GitHub :fontawesome-brands-github:](https://github.com/Dyalog/APLCourse/issues/new).
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # APL Course
2 | A self-study introduction to Dyalog APL with exercises. This course consists of a series of short instructional notes interleaved with problem sets.
3 |
4 | [View the course](https://course.dyalog.com)
5 |
6 | ## Getting Started
7 | If you're just starting out, you can use the [TryAPL](https://tryapl.org) online interpreter. Put TryAPL on one half of your screen, and view the course on the other half.
8 |
9 | Chapters and exercises involving the use of external files and some [system functions](./Quad%20names.md) require the full Dyalog system, which can be [downloaded for free from the Dyalog website](https://www.dyalog.com/download-zone.htm).
10 |
11 | If you need help typing APL glyphs (e.g. `×⌿⍳`) then see [the APL Wiki](https://aplwiki.com/wiki/Typing_glyphs#Prefix_key).
12 |
13 | ## Audience
14 | This course assumes high-school / secondary level mathematics knowledge, and some familiarity with basic programming terminology (e.g. *function*, *variable*, *recursion*). It is not intended as a general introduction to programming, but more of a fast-track to getting up and running with modern APL.
15 |
16 | ## Feedback
17 | If you have any suggestions, critcisms or praise, please [create an issue](https://github.com/Dyalog/APLCourse/issues/new).
18 |
19 | ## Technology
20 | This site is created using [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/).
--------------------------------------------------------------------------------
/docs/test.md:
--------------------------------------------------------------------------------
1 | # Test
2 | This is a page to test and demonstrate page elements. Some notes refer to the formatting of the markdown source.
3 |
4 | ## Code input
5 | ???+ Note "Okay"
6 | ```APL
7 | 1 2 3
8 | ```
9 | ```
10 | 1 2 3
11 | ```
12 | ---
13 | ```
14 | 4 5 6
15 | ```
16 | ```
17 | 4 5 6
18 | ```
19 | ---
20 | ```
21 | ⍝ no output needs empty block after
22 | ```
23 | ```
24 | ```
25 |
26 | 1. Some stuff
27 | 1. Code blocks in a list require a blank line above and below
28 |
29 | ```APL
30 | 1 2 3
31 | ```
32 | ```
33 | 1 2 3
34 | ```
35 | ---
36 | ```APL
37 | 4 5 6
38 | ```
39 | ```
40 | 4 5 6
41 | ```
42 |
43 | 1. And another thing
44 |
45 | Output with low characters followed by input.
46 |
47 | ```APL
48 | '⍝ Low Ɽ'
49 | ```
50 | ```
51 | ⍝ Low Ɽ
52 | ```
53 | ---
54 | ```APL
55 | 'HHH'
56 | ```
57 | ```
58 | HHH
59 | ```
60 | ---
61 | ```APL
62 | 1 2 3 + 4 5 6
63 | ```
64 | ```
65 | 5 7 9
66 | ```
67 | ---
68 | ```APL
69 | 'nested' 'vectors'
70 | ```
71 | ```
72 | ┌──────┬───────┐
73 | │nested│vectors│
74 | └──────┴───────┘
75 | ```
76 | ---
77 | ```APL
78 | 3 3⍴⍳9
79 | ```
80 | ```
81 | 1 2 3
82 | 4 5 6
83 | 7 8 9
84 | ```
85 |
86 | Text between code blocks must be surrounded by a blank line above and below.
87 |
88 | ```APL
89 | 2 3⍴,¨'APL' ⍝ Low Ɽ
90 | ```
91 | ```
92 | ┌─┬─┬─┐
93 | │A│P│L│
94 | ├─┼─┼─┤
95 | │A│P│L│
96 | └─┴─┴─┘
97 | ```
98 |
--------------------------------------------------------------------------------
/CI/check_version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # DCMS check_version.sh
3 | set -e
4 |
5 | # A local or jenkins builder will call this script from the root of the checked out git repo
6 | # Another build mechanism might copy files to another directory before modifying them
7 | if [ $# -eq 0 ];
8 | # $D is root directory of files to be injected, assuming they have the same relative structure as the original repository
9 | then
10 | D=$PWD # Assume we are in project root calling "./CI/inject_version.sh"
11 | else
12 | D=$1 # If argument given, that is new root dir of file to be injected.
13 | fi
14 |
15 | REPO_DIR="$(dirname $0)/../"
16 |
17 | VERSION_SOURCE="${D}/overrides/partials/footer.html"
18 |
19 | FILES="$VERSION_SOURCE"
20 | MISSING=""
21 |
22 | for FILE in $FILES
23 | do
24 | if [ ! -f $FILE ]; then
25 | echo "File not found: $FILE";
26 | MISSING="$MISSING $FILE";
27 | fi
28 | done
29 |
30 | # Previous version
31 | V0=`git show HEAD~1:overrides/partials/footer.html | grep -oE "([0-9]+)(nd|st) Edition, Revision ([0-9]+)" | grep -oE "([0-9]+)" | tr '\n' '.' | sed '$s/.$/\n/'`
32 |
33 | # New version
34 | V1=`cat overrides/partials/footer.html | grep -oE "([0-9]+)(nd|st) Edition, Revision ([0-9]+)" | grep -oE "([0-9]+)" | tr '\n' '.' | sed '$s/.$/\n/'`
35 |
36 | function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
37 |
38 | if [ $(version $V1) -le $(version $V0) ]; then
39 | printf "Please increment version number in $VERSION_SOURCE\nPrevious: $V0\nCurrent: $V1\n"
40 | exit 1
41 | fi
42 |
43 | echo ${V1}
44 |
45 |
46 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # CONTRIBUTING
2 | This course is written as markdown files which are rendered using MkDocs. If you would like to directly contributed, please [use the Fork and Pull Request workflow](https://gist.github.com/Chaser324/ce0505fbed06b947d962).
3 |
4 | ## Local Setup
5 | We use the [privacy plugin](https://squidfunk.github.io/mkdocs-material/plugins/privacy) to be GDPR compliant. Unfortunately, the MathJax library that is used to render mathematical notation is not compatible with the plugin, so we include a copy of the JavaScript in the built site (see the [gh-pages branch](https://github.com/Dyalog/APLCourse/tree/gh-pages)).
6 |
7 | We do not include it in the source here, so to correctly build the site for publication or preview, you must install MathJax into the **javascripts** directory first. We use npm to handle the MathJax dependency.
8 |
9 | ```sh
10 | cd APLCourse
11 | npm install
12 | mkdocs serve
13 | ```
14 |
15 | ## Copy input
16 | Code blocks are mostly styled to look like the Dyalog session, with six-space prompt input and flush-left output. We use custom css so that consecutive code blocks are visually merged, but only the input has a copy button.
17 |
18 | > Unfortunately, we haven't found a way to make this work in code blocks nested inside lists.
19 |
20 | To have an input (six-space prompt) which can be copied without the output, write them as separate code blocks:
21 | ```APL
22 | +/⍳10
23 | ```
24 | ```
25 | 55
26 | ```
27 |
28 | The `APL` qualifier means that blocks render with the class `language-APL` which can be used for syntax highlighting.
29 |
30 | To have multiple input-output blocks visually merged to the same input block, separate them with a horizontal rule:
31 | ```APL
32 | ]box on -trains=tree
33 | ```
34 | ```
35 | Was OFF -trains=box
36 | ```
37 | ---
38 | ```APL
39 | +⌿÷≢
40 | ```
41 | ```
42 | ┌─┼─┐
43 | ⌿ ÷ ≢
44 | ┌─┘
45 | ```
--------------------------------------------------------------------------------
/docs/style/main.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: APL;
3 | src: local("APL385 Unicode"), url("../assets/apl385.ttf");
4 | }
5 | .language-APL {
6 | font-family: APL!important;
7 | line-height: 1.2em!important;
8 | }
9 | code {
10 | font-family: APL;
11 | font-size: 1.03em!important;
12 | }
13 | .glyph {
14 | font-size: 1.5em;
15 | cursor: help;
16 | padding: 0 3px;
17 | }
18 | .md-logo > img{
19 | width: 7rem!important;
20 | height: 1.2rem!important;
21 | }
22 | @media screen and (max-width: 76.1875em) {
23 | .md-logo > img{
24 | width: 10rem!important;
25 | height: 1.8rem!important;
26 | }
27 | }
28 | /* Custom colors */
29 | :root {
30 | --md-primary-fg-color: #ED7F00;
31 | --md-primary-fg-color--dark: #563336;
32 | --md-default-bg-color: #F8F8F8;
33 | }
34 |
35 | /* Centering content */
36 | .centerText { text-align: center; }
37 | .center { display:inline-flex; justify-content: space-evenly; width: 100%; }
38 |
39 | .displayBox {
40 | border: solid 2px var(--md-primary-fg-color);
41 | padding: 0.3em 1em;
42 | border-radius: 0.3em;
43 | display: flex;
44 | justify-content: space-evenly;
45 | width: 80%;
46 | }
47 |
48 | /* Code copy only input (see CONTRIBUTING.md) */
49 | pre + pre > button, pre + p + hr {
50 | display: none!important;
51 | }
52 | pre > code {
53 | padding-bottom: 1em!important; /* Space below allows low hanging characters to be visible */
54 | line-height: 1.2em;
55 | font-size: 0.95em!important;
56 | }
57 | hr + p + pre > button {
58 | top: -0.2em !important; /* Move copy buttons in line with input */
59 | }
60 | pre + pre, hr + p + pre {
61 | margin-top: -1.8em!important; /* Visually merge consecutive code blocks */
62 | }
63 | pre + pre > code, hr + p + pre > code {
64 | box-sizing: border-box;
65 | padding-top: 0!important; /* Bring output lines closer to input lines */
66 | }
67 | dfn { font-weight: bold; }
68 |
69 | /* Footer */
70 | .aplcourse-version {
71 | width: auto;
72 | color: var(--md-footer-fg-color);
73 | font-size: .64rem;
74 | margin: auto 0.6rem;
75 | }
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: APL Course
2 | site_description: A guided introduction to Dyalog APL
3 | docs_dir: docs/
4 | site_url: https://dyalog.com
5 | markdown_extensions:
6 | - pymdownx.arithmatex:
7 | generic: true
8 | - pymdownx.highlight:
9 | use_pygments: false
10 | - attr_list
11 | - pymdownx.emoji:
12 | emoji_index: !!python/name:materialx.emoji.twemoji
13 | emoji_generator: !!python/name:materialx.emoji.to_svg
14 | - admonition
15 | - pymdownx.extra
16 | - pymdownx.details
17 | - pymdownx.superfences
18 | - abbr
19 | extra_javascript:
20 | - javascripts/config.js
21 | - javascripts/mathjax/tex-mml-chtml.js
22 | theme:
23 | name: material
24 | favicon: 'img/favicon-32.png'
25 | language: en
26 | logo: 'img/dyalog-white.svg'
27 | features:
28 | - navigation.instant
29 | - content.tooltips
30 | extra_css:
31 | - style/main.css
32 | extra:
33 | social:
34 | - icon: fontawesome/brands/dyalog
35 | link: https://dyalog.com
36 | - icon: material/elephant
37 | link: https://mastodon.social/@dyalog
38 | - icon: fontawesome/brands/twitter
39 | link: https://twitter.com/dyalogapl
40 | plugins:
41 | - privacy
42 | nav:
43 | - About: index.md
44 | - basic-syntax-and-arithmetic.md
45 | - dfns-and-assignment.md
46 | - array-logic-data-driven-conditionals.md
47 | - multidimensional-and-nested-arrays.md
48 | - cells-and-axes.md
49 | - finding-and-replacing-values.md
50 | - selecting-from-arrays.md
51 | - broken-keyboard-problems.md
52 | - loops-and-recursion.md
53 | - Extra Assignment: Assignment.md
54 | - Workspace Basics: Workspaces.md
55 | - Namespaces and Other Objects: Namespaces.md
56 | - Getting Help: Help.md
57 | - User-defined Functions: user-defined-functions.md
58 | - Operators: Operators.md
59 | - Quad Names: Quad names.md
60 | - error-handling-and-debugging.md
61 | - Data IO: Data.md
62 | - Code IO: Code.md
63 | - External Interfaces: Interfaces.md
64 | - Historical Quirks: Quirks.md
65 | - Interpreter Internals: Interpreter-internals.md
66 | - Further Reading: Reading.md
67 |
--------------------------------------------------------------------------------
/docs/examples.md:
--------------------------------------------------------------------------------
1 | # Introductory examples
2 |
3 | ## Histogram
4 | ```APL
5 | throws ← ?100⍴6
6 | counts ← +/(∪counts)∘.=counts
7 | hist ← `' ∘'[1+counts∘.>⍳⌈/counts]`
8 | ```
9 |
10 | ## Simple statistics
11 | ```APL
12 | students←⎕A
13 | scores←4 9 4 6 8 8 7 4 5 10 9 8 6 10 9 4 8 4 8 4 7 10 7 4 10 6
14 | classes←'ACABAAABCCABCBBBACBCCACABA'
15 |
16 | (score=⌈/score)/student
17 | classes{(+⌿⍵)÷≢⍵}⌸scores
18 | ```
19 |
20 | ## Grille cypher
21 | ```APL
22 | ⎕←(grid grille)←5 5∘⍴¨'VRYIALCLQIFKNEVPLARKMPLFF' '⌺⌺⌺ ⌺ ⌺⌺⌺ ⌺ ⌺ ⌺⌺⌺ ⌺⌺⌺ ⌺⌺'
23 | (' '=grille)/⍥,grid
24 | grid[⍸grille=' ']
25 | ```
26 |
27 | - matrices
28 | - compress
29 | - where `{⍵/⍳⍴⍵}`
30 | - indexing
31 |
32 | ## Before during after
33 | ```APL
34 | from_til ← 14 16
35 | times ← 14 19 11 15 15 18 12
36 | 'before' 'during' 'after'[1++⌿from_til∘.≤times]
37 | ┌──────┬─────┬──────┬──────┬──────┬─────┬──────┐
38 | │during│after│before│during│during│after│before│
39 | └──────┴─────┴──────┴──────┴──────┴─────┴──────┘
40 | 'before' 'during' 'after'[1+from_til⍸times]
41 | ┌──────┬─────┬──────┬──────┬──────┬─────┬──────┐
42 | │during│after│before│during│during│after│before│
43 | └──────┴─────┴──────┴──────┴──────┴─────┴──────┘
44 | ```
45 |
46 | - indexing
47 | - outer product
48 | - interval index/binning
49 | - related: histograms
50 |
51 | ## Fruits
52 | Anna, Ben and Charlie are having a competition. They want to see who can eat the most fruit in a week.
53 |
54 | ```APL
55 | fruits ← 4 7⍴'Apples MangoesOrangesBananas'
56 | days ← 7 3⍴'SunMonTueWedThuFriSat'
57 | names ← 3 7⍴'Anna Ben Charlie'
58 | ⎕RL ← 42 1 ⋄ ate ← ?3 4 7⍴3
59 | ```
60 |
61 | - high rank arrays
62 | - selection
63 | - summaries along different axes/ranks
64 |
65 | ## Take 4 words
66 | ```APL
67 | {⍵⌿⍨4>+\' '=⍵}'this is a sentence with seven words'
68 | this is a sentence
69 | {⊃(⊣,' ',⊢)/4↑' '(≠⊆⊢)⍵}'this is a sentence with seven words'
70 | this is a sentence
71 | ```
72 |
73 | ## Look and say
74 | ```APL
75 | {∊(≢,⊃)¨⍵⊂⍨1,2≠/⍵},1 3 3 3 3
76 | 1 1 4 3
77 | {⊃(//)↓⍉⍵⍴⍨2,⍨2÷⍨≢⍵}1 1 4 3
78 | 1 3 3 3 3
79 | {∊(//)⍵⍴⍨2,⍨2÷⍨≢⍵}1 1 4 3
80 | 1 3 3 3 3
81 | ```
--------------------------------------------------------------------------------
/docs/img/rectangle.svg:
--------------------------------------------------------------------------------
1 |
2 |
84 |
--------------------------------------------------------------------------------
/docs/img/line.svg:
--------------------------------------------------------------------------------
1 |
2 |
85 |
--------------------------------------------------------------------------------
/docs/Reading.md:
--------------------------------------------------------------------------------
1 | # Further Reading
2 |
3 | ## Activity
4 | If you want to continue learning, using and practising APL but don't have a particular goal in mind, here are some things you can do now.
5 |
6 | - Enjoy understanding articles on the APL Wiki. See [how to generate the APL Wiki logo as an svg](https://aplwiki.com/wiki/APL_Wiki_logo).
7 |
8 | - Try to solve problems from the [APL Problem Solving Competition](https://www.dyalog.com/student-competition.htm). Many of the past Phase I (simple) problems can be tried with instant feedback on [problems.tryapl.org](https://problems.tryapl.org).
9 |
10 | - There are many similar problem solving sites. Trying to solve these in APL is an excellent way to test and improve your skills.
11 | - [Project Euler](https://projecteuler.net/)
12 | - [Rosalind.info](http://rosalind.info/problems/list-view/)
13 | - [Code Golf Stack Exchange](https://codegolf.stackexchange.com/)
14 | - [Rosetta Code](http://www.rosettacode.org/wiki/Category:APL)
15 | - [LeetCode](https://leetcode.com/)
16 | - [Perl Weekly Challenge](https://perlweeklychallenge.org/)
17 |
18 | - Drill yourself on APL idiom knowledge with the [APLcart quiz](https://aplcart.info/quiz/).
19 | - Explore the language, don't forget the language bar and [basic help](/Help), summarised here:
20 | - [Dyalog Documentation Centre](https://docs.dyalog.com)
21 | - [Dyalog Online Help](https://help.dyalog.com/latest/)
22 | - [Dyalog Forums](https://forums.dyalog.com/)
23 | - [Stack Overflow](https://stackoverflow.com/questions/tagged/apl)
24 | - [The APL Orchard](https://apl.chat)
25 |
26 | ## Media
27 | We strongly recommend that you spend some time on a regular basis reading, watching, listening and exploring the existing APL media. As a language with a rich history, there have been a huge number of conferences, published papers and presentations where users and implementors discuss their activities and achievements.
28 |
29 | - [Dyalog.TV](https://dyalog.tv)
30 | - [The Array Cast Podcast](https://arraycast.com/)
31 | - [The Vector Journal of the British APL Association](https://vector.org.uk)
32 | - [APL Quote Quad](https://aplwiki.com/wiki/APL_Quote_Quad)
33 | - [APL Conferences](https://aplwiki.com/wiki/Conferences)
34 | - [Dyalog User Meetings](https://aplwiki.com/wiki/Dyalog_user_meeting)
35 | - [SIGAPL](http://www.sigapl.org/)
36 | - [Code Report videos featuring APL](https://www.youtube.com/watch?v=GZuZgCDql6g&list=PLA9gQgjzcpKEHpHWeu2MmNnTQSU6QSPBB)
37 | - [Rodrigo's YouTube channel](https://www.youtube.com/channel/UCd_24S_cYacw6zrvws43AWg)
38 | - [Videos by the late great John Scholes, inventor of dfns](https://www.youtube.com/watch?v=3qGsCrkWT-4&list=PLA9gQgjzcpKGxI9iCF-tvTuBthpR9QTVW)
--------------------------------------------------------------------------------
/docs/img/point.svg:
--------------------------------------------------------------------------------
1 |
2 |
85 |
--------------------------------------------------------------------------------
/docs/img/dyalog-white.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/docs/Help.md:
--------------------------------------------------------------------------------
1 | # Getting Help
2 | Following this course should give you at least an overview of all of the aspects of Dyalog which are needed to solve problems and build applications. By the end, hopefully you'll start to feel comfortable solving problems, reading and writing APL, and at least have an idea of where to look when you need to do some systems programming or interfacing with the outside world.
3 |
4 | To that end, here is a list of some of the resources available to you if you ever get stuck.
5 |
6 | ## What does this thing do?
7 | If you need help with a particular primitive or quad-name, the [Dyalog online help](https://help.dyalog.com/latest/) can answer *what does this do?*-style questions. Press `F1` in the session while highlighting a construct to go to its help page.
8 |
9 | ```APL
10 | ⍣ ⍝ Primitives
11 | ⎕THIS ⍝ Quad-names
12 | HTMLRenderer ⍝ Some keywords related to specific objects
13 | ```
14 | Many of the keywords which have documentation pages are the names of [GUI objects](http://help.dyalog.com/latest/#GUI/SummaryTables/GUIOverview.htm).
15 |
16 | It is also possible to set a custom URL to use for queries when the interpreter doesn't recognise something. In the IDE for Microsoft Windows, go to **Options → Configure → Help/DMX**. Tick "Use online help for non-Dyalog topics" and set the custom URL of your choice. For example, you can try using `https://aplcart.info/?q=%s`, so that F1 brings up the aplcart website search for that term.
17 |
18 | In the RIDE, F1 opens a browser window to the [Dyalog online documentation](https://help.dyalog.com/latest).
19 |
20 | The online help is a subset of the full materials available from the [Documentation Centre](https://docs.dyalog.com).
21 |
22 | !!! Warning "Version Information"
23 | Documentation for previously released versions of Dyalog is still available online.
24 |
25 | - [Full documentation for Dyalog version 12.1](https://docs.dyalog.com/12.1/)
26 | - [Online help system for 12.1](https://help.dyalog.com/12.1/)
27 | - [Full documentation for Dyalog version 17.1](https://www.dyalog.com/documentation_171.htm).
28 | - [Online help system for 17.1](https://help.dyalog.com/17.1/)
29 |
30 | ## How do I do this?
31 | This is a much more difficult thing to overcome. Sometimes you have an idea of what you want to achieve, but you either aren't sure what constructs are available to help you achieve it, or you aren't sure that the solution you've come up with is the best way to go about it.
32 |
33 | If you need help with *how to do something*, try searching in [APLcart](https://aplcart.info/), the searchable library of idiomatic expressions.
34 |
35 | ## Has this been done?
36 | We strongly recommend that you spend some time on a regular basis reading and exploring the existing APL media. As a language with a rich history, there have been a huge number of conferences, published papers and presentations where users and implementors discuss their activities and achievements. Some of these are listed on the [further reading](/Reading) page.
37 |
38 | ## Talk to humans
39 | - If you cannot find a solution on APLcart, please [ask on Stack Overflow](https://stackoverflow.com/questions/ask). There are a number of keen APLers who monitor this site and will eagerly answer any questions asked there.
40 | - Introduce yourself in [the APL Orchard](https://chat.stackexchange.com/rooms/52405/the-apl-orchard) Stack Exchange chat room, where you can usually get same-day replies to your queries. To get permission to post messages, see [apl.wiki/APL_Orchard#Access](https://apl.wiki/APL_Orchard#Access).
41 | - As well as Stack Overflow, the [Dyalog forums](https://forums.dyalog.com/) are full of interesting discussions and are quite active.
42 | - [The APL Wiki](https://aplwiki.com/) has hundreds of articles about both the history of APL, as well as specific language features and usage examples.
43 |
--------------------------------------------------------------------------------
/docs/Assignment.md:
--------------------------------------------------------------------------------
1 | # Assigning to arrays
2 |
3 | ## Indexed Assignment
4 | Assign values at specified indices.
5 |
6 | ```APL
7 | t←4 4⍴'some sample text'
8 | t[⍸t∊'aeiou']←'!'
9 | ```
10 |
11 | ## Selective Assignment
12 | Define `n←5 5⍴⍳25` in your workspace.
13 |
14 | 1. Using selections, find at least four different ways to set the bottom-right 3 by 3 submatrix in `n` to `0`.
15 | For example, `(2 2↓n)←0`.
16 |
17 | ??? Hint
18 | See which primitives may be used in a selective assignment
19 |
20 | ## Modified Assignment
21 | Experiment with the following expressions, paying particular attention to the `name f← array` construct.
22 |
23 | ```APL
24 | salaries←18250 42500 56000 57250 48640
25 | codes←'ACDDC'
26 | salaries×←1.1
27 | salaries[⍸codes='C']×←1.05
28 |
29 | a←⎕A
30 | (3↑a),←'abc'
31 | (¯4↑a),←'xyz' ⍝ this one will error — think about why!
32 | ```
33 |
34 | ## At
35 |
36 | Monadic functions take a single right argument array as input. Dyadic functions take two argument arrays.
37 |
38 | Monadic operators take a single left operand which can be a function or an array (as in `+/` where plus `+` is the function operand and reduce `/` is the operator).
39 |
40 | Dyadic operators take two operands which could be functions or arrays depending on the operator's definition. For example, the rank operator `F⍤k` takes a function left operand `F` and array right operand `k` of up to 3 elements.
41 |
42 | Selective and indexed assignment methods will change the values of variables. The "at" operator `@` merges two arrays at specified indices and returns a new array.
43 |
44 | If a function right operand returns a boolean array when applied to `⍵` (e.g. `3=1 3 5`) then ones `1` in the boolean array determine where scalars of `⍺` are inserted.
45 |
46 | ```APL
47 | ('∆⍥'@{⍵∊'AEIOU'})2 3⍴'DYALOG'
48 | (' '@2 3 4)'DYALOG'
49 | (' '@(1 2)(1 3)(2 1))2 3⍴'DYALOG'
50 | ```
51 |
52 | 1. The following expression contains an error:
53 | ` ('∆⍥'@1)2 3⍴'DYALOG'`
54 | Change the parenthesised function containing `@` in **two** ways so that it gives the following results:
55 | 1.
56 |
∆∆∆
57 | LOG
58 | 1.
59 |
∆∆∆
60 | ⍥⍥⍥
61 |
62 | Generally, the left operand to `@` is a function applied to scalars in `⍵` which are specified by a right operand that is either an array of scalar (simple or enclosed vector) indices or a boolean array returned by a right operand function. An array left operand is shorthand for a [constant function](https://aplwiki.com/wiki/Constant) that returns the array.
63 |
64 | ```APL
65 | {1↓1∘⎕C@(¯1⌽' '∘=)' ',⍵}'my excellent heading'
66 | ```
67 |
68 | ## Strand Assignment
69 | [**Distributed assignment**](http://help.dyalog.com/latest/#Language/Introduction/Namespaces/Distributed Assignment.htm) or **strand assignment** allows multiple names to be defined using a single assignment arrow `←`.
70 |
71 | ```APL
72 | (max min avg)←{(⌈⌿⍵)(⌊⌿⍵)((+⌿÷≢)⍵)}3 1 4 1 5
73 | ```
74 |
75 | !!! Note
76 | Strand assignment does not require names to be parenthesised, but we strongly recommend it for clarity.
77 |
78 | We can assign items in `nest` to the three variables `s←'A'` `v←1 2 3` and `m←3 3⍴⍳9` using a single assignment arrow.
79 |
80 | ```APL
81 | nest←('A'(1 2 3))(3 3⍴⍳9)
82 | ((s v) m)←nest
83 | ```
84 |
85 | !!! Warning
86 | You might have some issues when using inline, modified or strand assignment in dfns. This is by design, but can be a source of confusion.
87 |
{a←3 ⋄ f←+ ⋄ a f←3 ⋄ a}⍬
88 | 3
89 | a←3 ⋄ f←+ ⋄ a f←3 ⋄ a
90 | 6
91 |
92 | You can get around these problems by writing `∘⊢` (or in 12.1: `∘{⍵}` ) to the immediate right of any function involved:
93 |
{a←3 ⋄ f←+ ⋄ a f∘{⍵}←3 ⋄ a}⍬
94 | 6
95 |
96 |
--------------------------------------------------------------------------------
/docs/Interfaces.md:
--------------------------------------------------------------------------------
1 | # External Interfaces
2 | In an ideal world, we'd spend our entire lives blissfully in the warm comfort of the APL session. All of our data would magically appear and we would write perfect, beautiful algorithms to solve idealised problems.
3 |
4 | In the real world, we must interface with external systems. After all, the entire point of learning this tool is to use it to solve real world problems.
5 |
6 | ## Name Association
7 | Sufficiently knowledgeable programmers can interface directly between APL and a compiled library using `⎕NA`. Input and output data types must be explicitly declared. For more information see [the online documentation about ⎕NA](http://help.dyalog.com/latest/#Language/System%20Functions/na.htm).
8 |
9 | ## APL as a Shared Library
10 | It is also possible to bundle an APL application as a compiled native shared or static object (.dll, .dylib or .so) which exposes APL functions and makes them accessible via another programming language or application's [foreign function interface](https://en.wikipedia.org/wiki/Foreign_function_interface).
11 |
12 | Examples of usage and links to documentation are available on [github.com/Dyalog/NativeLib](https://github.com/Dyalog/NativeLib)
13 |
14 | ## Py'n'APL
15 | Start instances of Python from Dyalog, and start instances of Dyalog from Python. Leverage the convenience of Python's vast collection of libraries and combine it with the expressive power of APL. See [github.com/Dyalog/pynapl](https://github.com/Dyalog/pynapl) for more information.
16 |
17 | ## RSConnect and RConnect
18 | R is a very popular language with a large collection of statistical libraries. Kimmo Linna provides an open source interface to R using Rserve.
19 |
20 | [RSconnect R interface for Dyalog](https://github.com/kimmolinna/rsconnect).
21 |
22 | ## .NET
23 | Microsoft's .NET Framework contains a plethora of useful libraries for business applications.
24 |
25 | To enable reference to .NET namespaces, set `⎕USING`:
26 | ```APL
27 | ⎕USING←''
28 | System.TimeZone.CurrentTimeZone.StandardName
29 | GMT Standard Time
30 | ```
31 |
32 | Set the system variable `⎕USING` in order to access names within .NET namespaces.
33 | ```APL
34 | ⎕USING←'System'
35 | TimeZone.CurrentTimeZone.StandardName
36 | GMT Standard Time
37 | ```
38 |
39 | In recent years, Microsoft have been developing a cross-platform equivalent called .NET Core (or .NET 5). This allows the same libraries to be used on Microsoft Windows, macOS and Linux. It must be installed separately and enabled by setting the [configuration parameter](http://help.dyalog.com/latest/#UserGuide/Installation%20and%20Configuration/Configuration%20Parameters.htm) `DYALOG_NETCORE=1`.
40 |
41 | ## COM/OLE
42 | Dyalog is able to directly control certain Microsoft applications using the [Component Object Model](https://en.wikipedia.org/wiki/Component_Object_Model). For examples, see:
43 |
44 | - [Dyalog Webinar: APL and Microsoft Excel](https://dyalog.tv/Webinar/?v=hs90SdUc9dE)
45 | - [Document: Charting the APL/Excel waters](https://www.dyalog.com/uploads/conference/dyalog11/presentations/C05_using_excel_under_apl/officeauto11.pdf)
46 | - [Chapter 9 of the Dyalog for Microsoft Windows Interfaces Guide](https://docs.dyalog.com/latest/Dyalog%20for%20Microsoft%20Windows%20Interface%20Guide.pdf#page=185).
47 |
48 | ## ⎕NULL
49 | The core APL language does not have a "null" value as such. While you might think that the empty numeric vector `⍬` could be considered a type of "null", it already has type information associated with it so it doesn't really work - it is more accurate to call it an *empty numeric vector*. In order to cooperate with the COM and .NET interfaces described above, Dyalog has a proper null value which can be invoked with `⎕NULL`.
50 |
51 | ## Conga and HTTP
52 | [Conga]() is the core TCP/IP framework for Dyalog. On top of this, there are several higher level utilities for various web-based applications.
53 |
54 | [HttpCommand](../Data/#downloading-data-from-the-internet) can be used to issue requests to web servers and retrieve data.
55 |
56 | [Jarvis](https://github.com/Dyalog/Jarvis) is a very convenient way to expose APL functions as either a JSON or RESTful web service. It can even serve a simple static web interface and is the web service component of [TryAPL](https://tryapl.org).
57 |
58 | [DUI](https://github.com/Dyalog/DUI/) (Dyalog User Interface) is a cross-platform GUI framework for building web-based front-ends in APL. It includes its own web server, so the same code can be used for a standalone desktop application, web app and website. There is an example site for its predecessor (identical in most ways, but without standalone desktop deployment) called [MiServer](https://miserver.dyalog.com).
59 |
--------------------------------------------------------------------------------
/docs/todo.md:
--------------------------------------------------------------------------------
1 | # TODO
2 | - bracket axis versions of rank problems?
3 |
4 | - horizontal scroll on narrow partial code blocks
5 | - more description in [array logic problem 1](./array-logic-data-driven-conditionals.md#problem-set)
6 | - move finding and replacing problems interval index broken keyboard problems
7 | - link to section: http://localhost:8000/array-logic-data-driven-conditionals/#the-outer-product
8 | - outer product link: http://localhost:8000/basic-syntax-and-arithmetic/#singleton-extension
9 | - #TODO links
10 | - windows `2f/`
11 | - key
12 | - negative index generator link to problem: http://localhost:8000/basic-syntax-and-arithmetic/#what-do-these-errors-mean
13 | - http://localhost:8000/array-logic-data-driven-conditionals/#problem-set link to membership version in the solutions
14 | - [in problem set N](./array-logic-data-driven-conditionals.md#problem-set) (finding replacing)
15 | - proper treatment of scalar functions
16 | - put back info about the language bar and finding out what unfamiliar primitives do
17 | - Mean `{+⌿⍵÷≢⍵}` vs `{(+⌿⍵)÷≢⍵}`
18 | - find-replace-vals AnyVowels use Any idiom `∨/`
19 | - The idiom for joining a nested list into a simple list is `⊃,/`
20 | - outer product
21 | - identity matrix
22 | - countful membership
23 | - shape, reshape
24 | - look-and-say
25 | - take, drop
26 | - replicate/compress
27 |
28 | Perhaps many more of the functions should be introduced well before the rank operator.
29 |
30 | ## NOTES
31 | Bind essentially fills one of the “slots” of a dyadic function with an array value. 2⍴3 4 5 gives 3 4. The ⍵ is reshaped by the ⍺. If we fill the ⍵ slot, then we get a monadic function where the only argument must take the place of the ⍺ slot.
32 |
33 | ⍴∘3 4 5 ⍝ A monadic function which reshapes 3 4 5 according to ⍵
34 | (⍴∘3 4 5)2
35 | 3 4
36 | (⍴∘3 4 5)5
37 | 3 4 5 3 4
38 | (⍴∘3 4 5)¨2 5
39 | ┌───┬─────────┐
40 | │3 4│3 4 5 3 4│
41 | └───┴─────────┘
42 | Reshape (3 4 5) using each of 2 and 5
43 |
44 | ---
45 |
46 | 1. A company owns 5 stores which each sell the same 3 items.
47 |
48 | The quantity sold of each product in each store over a 7-day week are given in the array `qty`:
49 |
50 | ```APL
51 | ⎕RL←42 ⋄ qty←¯1+?5 3 7⍴10
52 | ```
53 |
54 | The prices of the three products are £4.99, £24.99 and £99.99.
55 |
56 | ```APL
57 | price ← .99 + 4 24 99
58 | ```
59 |
60 | The total costs each day to run each store are given in the matrix `cost`:
61 |
62 | ```APL
63 | ⎕RL←42 ⋄ costs ← 94+?5 7⍴11
64 | ```
65 |
66 | Each store has its own weekly profit target:
67 |
68 | ```APL
69 | target ← 3000 1250 800 6000 3200
70 | ```
71 |
72 | - what is the price of each item?
73 | £4.99 £24.99 £99.99
74 |
75 | - which day had the most?
76 | - which stores made at least their target profit?
77 |
78 | target←400
79 |
80 | --------
81 |
82 | 1. The game naughts and cross, also known as tic-tac-toe, is played on a 3 by 3 grid. Two players take turns placing their tokens on the grid until one player has made a complete line either horizontally, vertically or diagonally which consists of just that player's tokens.
83 |
84 | We can represent a game using a character matrix.
85 |
86 | 1.
87 |
88 | Now, instead of several 2-dimensional games, we will use a 3-dimensional array to represent a single 3-dimensional game.
89 |
90 | --------
91 |
92 | 1. These are the heights of some students in 3 classes. Students have numeric identifiers `id`.
93 | ```APL
94 | student ← 10 7⍴'Kane Jonah JessicaPadma Katie CharlieAmil David Zara Filipa '
95 | class ← 'CBACCCBBAB'
96 | height ← 167 177 171 176 178 164 177 177 173 160
97 | ```
98 |
99 | Use APL to:
100 |
101 | 1. Find the name of the tallest student
102 | 1. Find the class which class has the tallest average height
103 | 1. Find the class with the narrowest range of heights
104 |
105 | ## Making scalars
106 | 1. Turn the 1-element vector `v` into a scalar.
107 | 1. Write an expression using `⍴` which returns an empty numeric vector.
108 | 1. Write an expression using `⍳` which returns an empty numeric vector.
109 |
110 |
111 | ???+Example "Answer"
112 |
113 |
114 | The shape of a scalar is an empty numeric vector. We can therefore use an empty numeric vector as the left argument to the reshape function:
115 | ```APL
116 | ⍬⍴v
117 | ```
118 |
150 | If we can generate a length `n` vector with `⍳n`, what happens when `n=0`?
151 |
152 | ```APL
153 | ⍳0
154 | ```
155 | ```
156 |
157 | ```
158 |
159 |
160 |
--------------------------------------------------------------------------------
/docs/broken-keyboard-problems.md:
--------------------------------------------------------------------------------
1 | # Broken Keyboard Problems
2 | APL is a language which evolves over time. Some of the primitives available today exist because they represent patterns which are very common. Not only this, but in APL there are usually several equally good ways of expressing the same idea. As you develop competency in APL, this versatility in expression becomes an advantage.
3 |
4 | Imagine that your keyboard is broken such that it is impossible to type a particular glyph. It is a useful exercise in array thinking and use of APL to try and recreate the behaviour without using the primitive itself.
5 |
6 | For each of the following problems:
7 |
8 | - Read the prompt and think about which primitive function behaves in the way described.
9 | - Write a function which matches the description but does not use that primitive function.
10 |
11 | ---
12 |
13 | 1. This primitive function is used to find the indices of a Boolean array.
14 |
15 | ???Hint "What primitive function is this?"
16 | We want to model the **where** function `⍸⍵` without using the **iota-underbar** glyph `⍸`.
17 |
18 | ???Example "Answers"
19 | For Boolean vectors:
20 |
21 | ```APL
22 | {⍵/⍳≢⍵}
23 | {⍵/⍳⍴⍵}
24 | ```
25 |
26 | For Boolean arrays in general:
27 |
28 | ```APL
29 | {(,⍵)/,⍳⍴⍵}
30 | {⍵/⍥,⍳⍴⍵}
31 | ```
32 |
33 | 1. This primitive function counts the number of elements in a vector.
34 |
35 | ???Hint "What primitive function is this?"
36 | We want to model the **shape** `⍴⍵` or **tally** function `≢⍵` without using the **rho** `⍴` or **not-identical-to** `≢` glyphs. We might need to use different approaches depending on the type and structure of our argument.
37 |
38 | ???Example "Answers"
39 | For simple numeric vectors, we can use any mathemtical function which always returns `1` and add up the ones.
40 |
41 | ```APL
42 | {+/⍵÷⍵}
43 | {+/⍵*0}
44 | {+/1+0×⍵}
45 | {+/×1+|⍵}
46 | ```
47 |
48 | For any simple vector, we can ask equality with the argument itself:
49 |
50 | ```APL
51 | {+/⍵=⍵}
52 | ```
53 |
54 | For any vector, we can use each `⍺ F¨ ⍵` to map the match function `⍺≡⍵` between each element of `⍵` and itself.
55 |
56 | ```APL
57 | {+/⍵≡¨⍵}
58 | ```
59 |
60 | 1. This primitive function reverses the order of the elements in a vector.
61 |
62 | ???Hint "What primitive function is this?"
63 | We want to model the reverse function `⌽⍵` without using the circle-stile `⌽` glyph.
64 |
65 | ???Example "Answer"
66 | ```APL
67 | {⍵[1+(≢⍵)-⍳≢⍵]}
68 | {⍵[1+(⍴⍵)-⍳⍴⍵]}
69 | ```
70 |
71 | To model the reverse-first function `⊖⍵`, we should use **squad** `⍺⌷⍵` - the indexing function - to select major cells from our argument regardless of rank.
72 |
73 | ```APL
74 | {(⊂1+(≢⍵)-⍳≢⍵)⌷⍵}
75 | ```
76 |
77 | This solution still does not handle scalars. There may be several ways to account for this, but it easy to work around it with a guard:
78 |
79 | ```APL
80 | {0=≢⍴⍵: ⍵ ⋄ (⊂1+(≢⍵)-⍳≢⍵)⌷⍵}
81 | ```
82 |
83 | 1. Write a function to convert a character vector into upper case without using `⎕C`. Assume text consists of only lowercase alphabetic characters `a-z` and spaces.
84 |
85 | ```APL
86 | ToUpper 'sale on now'
87 | SALE ON NOW
88 | ```
89 |
90 | ???Example "Answer"
91 | ```APL
92 | ToUpper ← {(⎕A,' ')['abcdefghijklmnopqrstuvwxyz'⍳⍵]}
93 | ```
94 |
95 | To learn about case folding and mapping using the case conversion system function `⎕C`, watch the webinar [Language Features of Dyalog version 18.0 in Depth - Part 1](https://dyalog.tv/Webinar/?v=Hln3zryunsw).
96 |
97 | 1. Write a function to convert only lowercase alphabetic characters `a-z` into uppercase, and leave all others alone.
98 |
99 | ```APL
100 | text←'What? Ignore these $#!?# characters!?'
101 | ToUpper text
102 | ```
103 | ```
104 | WHAT? IGNORE THESE $#!?# CHARACTERS!?
105 | ```
106 |
107 | ???Example "Answer"
108 | There are other valid approaches, but here is one solution using selective assignment:
109 |
110 | ```APL
111 | ((text∊alph)/text) ← (⎕A,' ')[(text∊alph)/alph⍳text]
112 | ```
113 |
114 | 1. This function returns unique major cells of `⍵`.
115 |
116 | ???Hint "What primitive function is this?"
117 | We want to model the **unique** function `∪⍵` without using the **downshoe** `∪` glyph.
118 |
119 | ???Example "Answers"
120 | Index-of returns the index of the first occurance of an element. For `⍵⍳⍵`, this becomes a list of integer ID numbers which correspond to major cells as they appear.
121 |
122 | ```APL
123 | {((⍳≢⍵)=⍵⍳⍵)⌿⍵}
124 | ```
125 |
126 | This is a good opportunity to mention the **swap** `⍺ F⍨ ⍵` and **selfie** `F⍨⍵` operators.
127 |
128 | ```APL
129 | {⍵⌿⍨(⍳≢⍵)=⍳⍨⍵}
130 | ```
131 |
132 | 1. This primitive removes scalars in `⍵` from the vector `⍺`.
133 |
134 | ???Hint "What primitive function is this?"
135 | We want to Write the **without** function `⍺~⍵` without using the **tilde** `~` glyph.
136 |
137 | ???Example "Answer"
138 | ```APL
139 | {(1-⍺∊⍵)/⍺}
140 | ```
141 |
142 | 1. This primitive function returns elements in the vector `⍺` which are also found in the vector `⍵`.
143 |
144 | ???Hint "What primitive function is this?"
145 | We want to write the **intersection** function `⍺∩⍵` without using the **upshoe** `∩` glyph.
146 |
147 | ???Example "Answer"
148 | ```APL
149 | {(⍺∊⍵)/⍺}
150 | ```
151 |
152 | 1. Write a function which:
153 | - takes a numeric left argument vector `⍺`, sorted ascending
154 | - takes a numeric right argument vector `⍵`
155 | - returns an integer vector of the same length as `⍵`, indicating the intervals in `⍺` in which elements in `⍵` belong
156 |
157 | ```APL
158 | 1 3 7 Function 0 2 3 5 8
159 | 0 1 2 2 3
160 | ```
161 |
162 | ???Hint "What primitive function is this?"
163 | Write a model of the **interval index** function `⍺⍸⍵` without using the **iota-underbar** `⍸` glyph.
164 |
165 | ???Example "Answer"
166 | For numeric arguments:
167 |
168 | ```APL
169 | {+⌿⍺∘.≤⍵}
170 | {+/⍵∘.≥⍺}
171 | ```
172 |
173 | 1. This primitive function returns `¯1` for negative numbers, `0` for `0` and `1` for positive numbers in its numeric argument array.
174 |
175 | ???Hint "What primitive function is this?"
176 | Write the **sign** or **signum** function `×⍵` without using the **multiplication** `×` glyph.
177 |
178 | ???Example "Answer"
179 |
180 | ```APL
181 | {(0<⍵)-(0>⍵)}
182 | ```
183 |
--------------------------------------------------------------------------------
/docs/dfns-and-assignment.md:
--------------------------------------------------------------------------------
1 | # Dfns and Assignment
2 |
3 | ## Dfns
4 | A dfn (pronounced "*dee-fun*" with a very short "u" sound) is a way of writing functions in APL. It starts and ends with curly braces `{}`, has a right argument `⍵` (omega) and an optional left argument `⍺` (alpha).
5 |
6 | ```APL
7 | 3{⍺}5 ⍝ ⍺ is the (optional) left argument
8 | 3
9 | {⍵}'apl' ⍝ ⍵ is the right argument
10 | apl
11 | {⍺}5 ⍝ Calling a dyadic function monadically results in an error
12 | VALUE ERROR
13 | {⍺}5
14 | ∧
15 | 3{⍵} ⍝ Calling a function without a right argument results in an error
16 | SYNTAX ERROR: Missing right argument
17 | 3{⍵}
18 | ∧
19 | ```
20 |
21 | From here, when functions are first introduced, `F⍵` ("eff omega") denotes a monadic function `F` and `⍺F⍵` ("alpha eff omega") denotes a dyadic function.
22 |
23 | ## Assignment
24 | Names are assigned with the left arrow `name ← expression`. We say "name gets [function or array]".
25 |
26 | ```APL
27 | one←1
28 | three←3
29 | equals←=
30 | plus←+
31 | four←4
32 | four equals one plus three ⍝ 1 means true, 0 means false
33 | ```
34 | ```
35 | 1
36 | ```
37 |
38 | We can use a name in the same line in which it is defined. In production code it is best to avoid this unless an expression is very short.
39 |
40 | Read the following as "squared numbers divided by the sum of squares":
41 | ```APL
42 | squared÷+/squared←¯1 0 1 2*2
43 | ```
44 | ```
45 | 0.1666666667 0 0.1666666667 0.6666666667`
46 | ```
47 |
48 | ## Syntactic name class
49 | You may come across the following error:
50 |
51 | ```APL
52 | count ← {+/⍵}
53 | count ← {+/⍵} 1 0 0 1 0 1 0
54 | ```
55 | ```
56 | SYNTAX ERROR: Invalid modified assignment, or an attempt was made to change nameclass on assignment
57 | count←{+/⍵}1 0 0 1 0 1 0
58 | ∧
59 | ```
60 |
61 | Things in APL have both a word and a number which identifies what type of thing it is. This is called its [**name class**](http://help.dyalog.com/latest/#Language/System%20Functions/nc.htm). So far we have met **variables** (nameclass 2) and **functions** (nameclass 3). There are more than these, but they will be introduced in relevant chapters.
62 |
63 | In Dyalog APL, if a name already has a function assigned, that same name cannot then be assigned an array value. Nor vice versa. If this happens, erase the name and try again.
64 |
65 | ```APL
66 | )ERASE count
67 | count←{+/⍵}1 0 0 1 0 1 0
68 | count
69 | 3
70 | ```
71 |
72 | !!!Question "What is this `)ERASE` thing?"
73 | We have just used a system command. They are available while using the Dyalog interpreter or TryAPL interactively. However, they cannot be used inside functions and they are not standard APL syntax. In the section on [system functions and system commands](./Workspaces.md#system-commands) we will learn about things like showing a list of the currently defined names and how to erase names programmatically (there is a system function `⎕EX`). In the meantime, we will introduce system functions and commands as needed.
74 |
75 | ## Multiline functions and the editor
76 | You can do quite a lot in a single line of APL. However, it is not long before you want to keep sequences of multiple statements available for re-use. Of course we can write functions which consist of multiple statements.
77 |
78 | The statement separator, `⋄` (diamond), allows us to write multiple APL statements in a single line. Some people think that it is more readable to spread multiple statements across multiple lines of a function. However, it is worth being aware that APL diamonds `⋄` are equivalent to newline characters in terms of execution. The following two definitions of the `Mean` function are equivalent.
79 |
80 | ```APL
81 | Mean ← {
82 | sum ← +/⍵
83 | count ← ≢⍵
84 | sum ÷ count
85 | }
86 |
87 | Mean ← { sum ← +/⍵ ⋄ count ← ≢⍵ ⋄ sum÷count }
88 | ```
89 |
90 | Separate statements are executed from left to right and top to bottom.
91 |
92 | **To edit multiline functions** in the IDE for Microsoft Windows and the RIDE, invoke the editor with the system command `)ED`. You can find a step-by-step example of creating a multiline function in the Dyalog editor in [chapter 5 of Mastering Dyalog APL](https://mastering.dyalog.com/User-Defined-Functions.html?highlight=editor#a-working-example).
93 |
94 | **On TryAPL**, the current execution block can be continued on to a new line using Alt+Enter. The continuation line begins with a tab character. To execute the block, simply press Enter after your final line is typed. Here is an example defining a multiline dfn:
95 |
96 | 1. Type `Sum ← {` and press Alt+Enter
97 | 2. Type `⍺+⍵` and press Alt+Enter
98 | 3. Type `}` and press just Enter
99 | 4. The function `Sum` is now defined in your workspace. Try the expression `3 Sum 4`.
100 |
101 | ## Problem set 2
102 | The following problems can be solved with single-line dfns.
103 |
104 | 1. Eggs
105 |
106 | A recipe serving 4 people uses 3 eggs. Write the function `Eggs` which computes the number of eggs which need cracking to serve `⍵` people. Using a fraction of an egg requires that a whole egg be cracked.
107 |
108 | ```APL
109 | Eggs 4
110 | ```
111 | ```
112 | 3
113 | ```
114 | ---
115 | ```APL
116 | Eggs 100
117 | ```
118 | ```
119 | 75
120 | ```
121 | ---
122 | ```APL
123 | Eggs ⍳12
124 | ```
125 | ```
126 | 1 2 3 3 4 5 6 6 7 8 9 9
127 | ```
128 |
129 | ???Example "Answer"
130 | ```APL
131 | Eggs ← {⌈⍵×3÷4}
132 | ```
133 |
134 | 1. Write a function `To` which returns integers from `⍺` to `⍵` inclusive.
135 |
136 | ```APL
137 | 3 To 3
138 | 3
139 | 3 To 4
140 | 3 4
141 | 1 To 7
142 | 1 2 3 4 5 6 7
143 | ¯3 To 5
144 | ¯3 ¯2 ¯1 0 1 2 3 4 5
145 | ```
146 |
147 | **BONUS:** What if `⍺>⍵`?
148 | ```APL
149 | 3 To 5
150 | 3 4 5
151 | 5 To 3
152 | 5 4 3
153 | 5 To ¯2
154 | 5 4 3 2 1 0 ¯1 ¯2
155 | ```
156 |
157 | ???Example "Answer"
158 | In the simple case, make sure to generate enough numbers and use `⍺` as an offset:
159 | ```APL
160 | To ← {⍺+¯1+⍳1+⍵-⍺}
161 | ```
162 | In general we take into account whether the difference is positive or negative:
163 | ```APL
164 | To ← {⍺+(×d)ׯ1+⍳1+|d←⍵-⍺}
165 | ```
166 |
167 | 1. The formula to convert temperature from Celsius ($T_C$) to Fahrenheit ($T_F$) in traditional mathematical notation is as follows:
168 |
169 | $$T_F = {32 + {{9}\over{5}}\times {T_C}}$$
170 |
171 | Write the function `CtoF` to convert temperatures from Celcius to Farenheit.
172 | ```APL
173 | CtoF 11.3 23 0 16 ¯10 38
174 | 52.34 73.4 32 60.8 14 100.4
175 | ```
176 |
177 | ???Example "Answer"
178 | ```APL
179 | CtoF ← {32+⍵×9÷5}
180 | ```
181 |
182 | 1. Prime Time
183 |
184 | A prime number is a positive whole number greater than $1$ which can be divided only by itself and $1$ with no remainder.
185 |
186 | Write a dfn which returns `1` if its argument is prime and `0` otherwise.
187 |
188 | IsPrime 21
189 | 0
190 | IsPrime 17
191 | 1
192 |
193 | ???Example "Answer"
194 | There are several ways to code this, but the basic method is to count the number of divisors.
195 | ```APL
196 | IsPrime ← {2=+/d=⌊d←⍵÷⍳⍵}
197 | IsPrime ← {2=+/0=(⍳⍵)|⍵}
198 | ```
--------------------------------------------------------------------------------
/docs/Quad names.md:
--------------------------------------------------------------------------------
1 | # Quad names
2 |
3 | ## Overview
4 | There is a [vendor-agnostic](https://aplwiki.com/wiki/List_of_language_developers) article about [quad-names on the APL Wiki](https://aplwiki.com/wiki/Quad_name). This page is an overview focusing on application development with Dyalog.
5 |
6 | See the Dyalog online documentation for:
7 |
8 | - [a complete list of categorised system functions with descriptions](http://help.dyalog.com/18.0/#Language/System%20Functions/Summary%20Tables/System%20Functions%20Categorised.htm)
9 | - [a complete list of system variables with descriptions](http://help.dyalog.com/18.0/#Language/System%20Functions/Summary%20Tables/System%20Variables.htm)
10 | - [a complete list of system functions and variables](http://help.dyalog.com/latest/#Language/System%20Functions/Summary%20Tables/System%20Functions%20and%20Variables%20ColWise.htm)
11 |
12 | ## System variables
13 | The Dyalog online documentation has [a complete list of system variables](http://help.dyalog.com/latest/#Language/System%20Functions/Summary%20Tables/System%20Variables.htm).
14 |
15 | System variables describe the state of the system.
16 | Some variables are static and cannot change.
17 | Some variables are dynamic and can change without direct user intervention.
18 | The others can be changed by the user.
19 |
20 |
25 |
26 |
27 |
28 |
⎕A
29 |
⎕DM
30 |
⎕DMX
31 |
⎕PATH
32 |
⎕SM
33 |
34 |
35 |
⎕TRAP
36 |
⎕AN
37 |
⎕EN
38 |
⎕PP
39 |
⎕STACK
40 |
41 |
42 |
⎕TS
43 |
⎕AV
44 |
⎕FR
45 |
⎕PW
46 |
⎕TC
47 |
48 |
49 |
⎕USING
50 |
⎕AVU
51 |
⎕IO
52 |
⎕RL
53 |
⎕THIS
54 |
55 |
56 |
⎕WA
57 |
⎕CT
58 |
⎕LC
59 |
⎕RSI
60 |
⎕TID
61 |
62 |
63 |
⎕WSID
64 |
⎕D
65 |
⎕LX
66 |
⎕RTL
67 |
⎕TNAME
68 |
69 |
70 |
⎕WX
71 |
⎕DCT
72 |
⎕ML
73 |
⎕SE
74 |
⎕TNUMS
75 |
76 |
77 |
⎕XSI
78 |
⎕DIV
79 |
⎕NULL
80 |
⎕SI
81 |
⎕TPOOL
82 |
83 |
84 |
85 | Of course, the majority of the time you can refer to the [help system](../Help/#what-does-this-thing-do) to remind yourself exactly how each of these works. There are many system variables built up over the decades, many of which are kept mostly for backwards compatibility.
86 |
87 | In the following table, the system variables you are most likely to come across in existing code are highlighted in red.
88 |
89 |
93 |
94 |
95 |
96 |
⎕A
97 |
⎕DM
98 |
⎕DMX
99 |
⎕PATH
100 |
⎕SM
101 |
102 |
103 |
⎕TRAP
104 |
⎕AN
105 |
⎕EN
106 |
⎕PP
107 |
⎕STACK
108 |
109 |
110 |
⎕TS
111 |
⎕AV
112 |
⎕FR
113 |
⎕PW
114 |
⎕TC
115 |
116 |
117 |
⎕USING
118 |
⎕AVU
119 |
⎕IO
120 |
⎕RL
121 |
⎕THIS
122 |
123 |
124 |
⎕WA
125 |
⎕CT
126 |
⎕LC
127 |
⎕RSI
128 |
⎕TID
129 |
130 |
131 |
⎕WSID
132 |
⎕D
133 |
⎕LX
134 |
⎕RTL
135 |
⎕TNAME
136 |
137 |
138 |
⎕WX
139 |
⎕DCT
140 |
⎕ML
141 |
⎕SE
142 |
⎕TNUMS
143 |
144 |
145 |
⎕XSI
146 |
⎕DIV
147 |
⎕NULL
148 |
⎕SI
149 |
⎕TPOOL
150 |
151 |
152 |
153 | ### Constant
154 | ```APL
155 | ⎕A ⍝ Upper-case alphabet
156 | ABCDEFGHIJKLMNOPQRSTUVWXYZ
157 | ⎕D ⍝ Digits 0-9
158 | 0123456789
159 | ⎕AN ⍝ User's ID
160 | dyalog
161 | ⎕NULL ⍝ NULL constant
162 | [Null]
163 | ⎕TC ⍝ BackSpace, LineFeed, CarriageReturn
164 |
165 |
166 |
167 | ⎕UCS⎕TC ⍝ BackSpace, LineFeed, CarriageReturn
168 | 8 10 13
169 | ```
170 |
171 | ### Dynamic
172 | ```APL
173 | ⎕AV ⍝ List of APL characters
174 | ⎕DM ⍝ Last error message
175 | ⎕DMX ⍝ ⎕DM+⎕EN in a thread safe form
176 | ⎕EN ⍝ Last error number
177 | ⎕RTL ⍝ Response time limit
178 | ⎕SE ⍝ The session object
179 | ⎕TS ⍝ Current date/time
180 | ⎕THIS ⍝ Current object
181 | ```
182 |
183 | ### Settable
184 | ```APL
185 | ⎕AVU characters to use in ⎕AV
186 | ⎕CT Comparison Tolerance
187 | ⎕DCT Decimal Comparison Tolerance
188 | ⎕DIV how to handle division by 0
189 | ⎕FR float decimal system in use
190 | ⎕IO index origin
191 | ⎕ML Migration Level
192 | ⎕LX Latent eXpression
193 | ⎕PATH where to find functions
194 | ⎕RL random number generation seed value
195 | ```
196 |
197 | ### Division control
198 | ```APL
199 | ⎕DIV←0 ⍝ Default
200 | 3÷0
201 | DOMAIN ERROR: Divide by zero
202 | 3÷0
203 | ∧
204 | 0÷0
205 | 1
206 | ⎕DIV←1
207 | 3÷0
208 | 0
209 | 0÷0
210 | 0
211 | ```
212 |
213 | ### Print Precision
214 | The number of significant digits in the display of numeric output.
215 | ```APL
216 | ⎕PP←3
217 | ÷812
218 | 0.00123
219 | ≢'123'
220 | 3
221 | ⎕PP←17
222 | ÷812
223 | 0.0012315270935960591
224 | ≢'12315270935960591'
225 | 17
226 | ```
227 |
228 | ### Print Width
229 | The Print Width `⎕PW` sets the number of characters in the session before wrapping to a new line.
230 |
231 | It can be assigned to directly, or set to automatically adjust based on the IDE windows size. In the Microsoft Windows IDE, go to **Options**→**Configure**→**Session** and tick **Auto PW**. In the RIDE, go to **Edit**→**Preferences**→**General**→**Session** and tick **Auto PW**.
232 |
233 | ## System functions
234 | In this course, we try to introduce relevant quad-names in appropriate contexts. However, not every quad-name is shown in this tutorial.
235 |
236 | A complete collection of categorised system functions is available [from the Dyalog online documentation](http://help.dyalog.com/latest/#Language/System%20Functions/Summary%20Tables/System%20Functions%20Categorised.htm?Highlight=system%20function).
237 |
238 | Further treatment of system functions is provided in [Chapter L of Mastering Dyalog APL](https://www.dyalog.com/uploads/documents/MasteringDyalogAPL.pdf#%5B%7B%22num%22%3A927%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C69%2C640%2C0%5D).
239 |
--------------------------------------------------------------------------------
/docs/Workspaces.md:
--------------------------------------------------------------------------------
1 | # Workspace basics
2 | We sure have made a lot of functions so far and we've typed many expressions into our [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop). There seem to be a few variables in our workspace as well. We should save them somewhere for later.
3 |
4 | *[REPL]: Read Evaluate Print Loop
5 |
6 | ## What's a workspace?
7 | If you have been using Dyalog, the **session log** is the page with all of your input and output so far. You can scroll up the session log (with a mouse or using the Page Up key) and see everything you have done so far.
8 |
9 | A **workspace** is a collection of names. We can obtain some lists of names using **system commands**.
10 |
11 | ```APL
12 | )fns ⍝ Functions
13 | )vars ⍝ Variables (arrays)
14 | ```
15 |
16 | These commands have the special `)COMMAND` syntax, and are only used when interacting with the session. They return no result and cannot be used programmatically; they cannot be used in a function.
17 |
18 | ## What's in a workspace?
19 |
20 | - `]Map`
21 |
22 | See a diagram indicating the types of names in the current namespace.
23 | Also use the Workspace Explorer: go to **Tools → Explorer** in the Microsoft Windows IDE or **View → Show Workspace Explorer** in RIDE.
24 |
25 | - `]Locate`
26 |
27 | Search and replace strings (including function names, literal character vectors and comments) in functions, operators, namespaces and other objects. It does not search inside character array variables.
28 | You can also use **Tools → Search** in the Windows IDE.
29 |
30 | - `]Peek`
31 |
32 | Try an expression as if it was executed in a saved workspace without having to copy the contents of that workspace.
33 |
34 | ```APL
35 | ]peek dfns cal 2021 7
36 | ]peek -?
37 | ```
38 |
39 | ## How big is a workspace?
40 | The data and code in the active workspace is limited to the maximum workspace size, or **MAXWS** (*maks-wuss*). The size of a **.dws workspace file** is usually much smaller than this.
41 |
42 | We can get the current value:
43 | ```APL
44 | ⎕←2⎕NQ'.' 'GetEnvironment' 'MAXWS'
45 | ```
46 |
47 | The **maximum workspace size** can be set to a different value using the MAXWS [configuration parameter](https://help.dyalog.com/latest/index.htm#UserGuide/Installation%20and%20Configuration/Configuration%20Parameters.htm). If you are using the Microsoft Windows IDE, you can go to **Options → Configure → Workspace** and set the maximum workspace size. In either case, the interpreter must be restarted for the change to take effect.
48 |
49 | The MAXWS setting is an adjustable software limitation, although there is also a hardware limitation: the amount of [memory](https://kb.iu.edu/d/ahtx) in the computer.
50 |
51 | Finally, you can see how much workspace is available with `⎕WA`.
52 |
53 | ## System commands
54 | A [table of system commands](https://help.dyalog.com/latest/index.htm#Language/System%20Commands/Introduction.htm) is provided in the online documentation.
55 |
56 | The *session* is sometimes used to refer to the interactive mode of operation (also known as *calculator mode* also known as *immediate execution mode*), in contrast to *under program control*, which is when something happens due to a line of code in a program/function.
57 |
58 | For example:
59 |
60 | ```APL
61 | myvar ← 2×⍳3 ⍝ Declare a variable in the session
62 | )erase myvar ⍝ Use a system command to erase the variable
63 | ```
64 |
65 | If we try to use a system command inside a function, it won't work.
66 |
67 | ```APL
68 | ⍝ The ]DInput user command lets us write mult-line dfns in the session
69 | ]dinput ⍝ Alternatively, press Shift+Enter with the cursor | on a name
70 | MultiFn←{ ⍝ A multi-line dfn
71 | ⍝ These statements are executed "under program control"
72 | ⎕←5+5
73 | var ← 2+2 ⍝ This variable only exists when this function is running
74 | )erase var ⍝ This won't work
75 | }
76 | ⍝ Now try to execute:
77 | MultiFn ⍬
78 | 10
79 | VALUE ERROR: Undefined name: erase
80 | MultiFn[4] )erase var ⍝ This won't work
81 | ∧
82 | ```
83 |
84 | !!! Note
85 | Attempting to execute the above `MultiFn` function will cause the tracer to open by default. Simply press Esc to quit the suspended function and return to the session.
86 |
87 | ## System Functions
88 | Some [**quad-names**](../Quad names) are [**system variables**](../Quad names/#system-variables), such as `⎕A`, `⎕D` and `⎕AV`. Others are [**system functions**](../Quad names/#system-functions), many of which are similar to system command counterparts.
89 |
90 | |System Command|System Function|
91 | |---|---|
92 | |`)SAVE /path/to/WorkspaceFile`|`⎕SAVE'/path/to/WorkspaceFile'`|
93 | |`)LOAD /path/to/WorkspaceFile`|`⎕LOAD'/path/to/WorkspaceFile'`|
94 | |`)ERASE name`|`⎕EX'name'`|
95 |
96 | !!! Note
97 | ⎕SAVE will overwrite any existing workspace file without asking first. Use )SAVE when saving workspaces.
98 |
99 | In contrast to the system commands, which can only be used in the interactive session, system functions can be used in a function (A.K.A. *under program control*).
100 |
101 | [**System functions**](../Quad names/#system-functions) are in-built functions with names of the form `⎕FUNCTION` and *do* return a result. Some have shy results which can be used by subsequent functions, or printed to the session output with `⎕←` (*quad-gets*).
102 |
103 | ```APL
104 | multifn←{
105 | ⍝ These statements are executed "under program control"
106 | ⎕←5+5
107 | var ← 2+2 ⍝ This variable only exists when this function is running
108 | ⎕EX 'var' ⍝ This will work, although it does not do anything useful in this dfn
109 | }
110 | ```
111 |
112 | The Name List `⎕NL` function lists names according to their [*name class*](http://help.dyalog.com/18.0/#Language/System Functions/nc.htm).
113 |
114 | ```APL
115 | ⎕NL 2 ⍝ List variables as a text matrix
116 | ⎕NL 3 ⍝ List functions
117 | ⎕NL-⍳9 ⍝ List all names as a nested vector of character vectors
118 | ```
119 |
120 | ### ⎕CLEAR
121 | Prank your friends with the best function ever:
122 | ```APL
123 | BestFunctionEver←{
124 | _←⎕SAVE'/tmp/','_'@(' '∘=)⍕⎕TS
125 | ⎕CLEAR
126 | }
127 | ```
128 |
129 | ### ⎕OFF
130 | An event better function for pranks:
131 | ```APL
132 | BestFunctionEver←{
133 | _←⎕SAVE'/tmp/','_'@(' '∘=)⍕⎕TS
134 | ⎕OFF
135 | }
136 | ```
137 |
138 | `⎕OFF` can also emit custom exit codes. Standard Dyalog exit codes are:
139 |
140 | - 0: Successful exit from `⎕OFF`, `)OFF`, `)CONTINUE` or graphical exit from the GUI
141 | - 1: APL failed to start (for example: lack of memory, bad translate table in Classic)
142 | - 2: APL received a [SIGHUP or SIGTERM](https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html).
143 | - 3: APL generated a syserror
144 |
145 | ## Saving and loading
146 | The example below shows how to save and load a workspace.
147 |
148 | ```APL
149 | ]cd /tmp
150 | )save MyFirstWS
151 | )clear
152 | )load MyFirstWS
153 | ```
154 |
155 | ## Uses of workspaces
156 |
157 | - **Distribution:** For large applications, it will be inconvenient or undesirable to ship large [collections of source files](../Code/#source-code-in-text-files) that are loaded at startup. Workspaces are often used as a mechanism for the distribution of packaged collections of code and data.
158 | - **Crash Analysis:** When an application fails, it is often useful to save the workspace, complete with execution stack, code and data, for subsequent analysis and sometimes resumption of execution.
159 | - **Pausing work:** In many ways, this is similar to crash analysis: sometimes you need to shut down your machine in the middle of things and resume later, but you don't want to be forced to start from scratch because you have created an interesting scenario with data in the workspace. Saving a workspace allows you to do this.
160 |
161 | *[SCM]: Source Code Manager
162 |
163 | ## Activities
164 |
165 | 1. What is the rank of `⎕NL x` for any scalar or vector `x`?
166 | 1. What is the rank of `⎕NL -x` for any scalar or vector `x`?
167 | 1. Save Your Work
168 | 1. Use `]cd` to change to a directory on your machine where you would like to save your work
169 | 1. Use `)wsid WSName` to change the name of your active workspace
170 | 1. Use `)save` to save your workspace
171 |
172 | !!! Note
173 | ⎕SAVE will overwrite any existing workspace file without asking first. Use )SAVE when saving workspaces.
174 |
--------------------------------------------------------------------------------
/docs/img/numerical-integration.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/Code.md:
--------------------------------------------------------------------------------
1 | # Importing, Exporting and Distributing Code
2 | We have already learned [the basics of saving and loading workspaces](/Workspaces). Here we address some more specific things like how to use other people's code, how to let other people use your code, and how to distribute your application to end users.
3 |
4 | ## Installed Libraries
5 | Dyalog installations come with a suite of libraries with pre-written code utilities and examples.
6 |
7 | Find which ones are available to you now with `)lib`.
8 |
9 | There is also a [**code libraries reference guide**](http://docs.dyalog.com/17.1/Code%20Libraries%20Reference%20Guide.pdf) in the documentation.
10 |
11 | You can copy code into the workspace in several ways:
12 |
13 | - Copy the entire library into the current namespace (use pcopy to ensure that existing names are not overwritten):
14 |
15 | `)pcopy dfns`
16 |
17 | - Copy selected functions into the current namespace:
18 |
)copy dfns cal date days
19 | cal 2↑⎕ts
20 |
21 | - Copy the entire library into a specific namespace:
22 |
'dfns'⎕ns⍬
23 | dfns.⎕cy'dfns'
24 |
25 | - Copy the selected functions into a specific namespace:
26 |
'dfns'⎕ns⍬
27 | 'cal' 'date' 'days'dfns.⎕cy'dfns'
28 |
29 | Alongside [APLcart](https://aplcart.info), the [dfns library](https://dfns.dyalog.com) (not to be confused with the dfns construct) contains a large number of useful functions for use cases ranging from number theory, graphs and tree data structures to games and graphics.
30 |
31 | ## User Commands
32 | Some of these, such as the `]box` one, have been mentioned already. Commands which begin with a right-square-bracket `]` are called User Commands. These are only used while interacting with the session, but you can customise them and create your own.
33 |
34 | Dyalog webinar: Creating and Managing your own User Commands
35 |
36 | Custom user commands are scripted classes or namespaces containing specific member functions `List`, `Run` and `Help`. They should be saved as plain text **.dyalog**, **.apln** or **.aplc** files and placed in the folder **[HOME]/MyUCMDs** where \[HOME\] is either */home/user* on Unix-like systems or *C:\Users\user\Documents* on Microsoft Windows.
37 |
38 | Some particularly useful in-built user commands for getting information about your workspace are mentioned in the section on [workspaces](../Workspaces).
39 |
40 | ## Creating and sharing your own utilities
41 | The user command system is designed for utilities to help during application development and debugging. It is not intended as a system for programmatic utilities. Due to its terse nature, APL vendors have not really established a full-fledged, public package management system like Python's pip or Node/JavaScript's npm. Usually, the source code is distributed either in code files, workspaces or in text files and copied wholesale into other code bases.
42 |
43 | However, you might find or develop utility functions which you use frequently and that you copy into code bases frequently. In this case, you might like to make such utilities easy to access. One option is to define the function as a member of a custom namespace within the session namespace `⎕SE`.
44 |
45 | Here is an example of using this technique so that you don't have to write such a long function reference to use `repObj`.
46 |
47 | ```APL
48 | 'X'⎕SE.⎕NS⍬
49 | ⎕SE.X.rep←⎕SE.Dyalog.Utils.repObj
50 | ```
51 |
52 | Once your custom name (function, variable etc.) has been defined within ⎕SE, save the session file. In the Microsoft Windows IDE, go to **Session → Save** to overwrite the default session file. In general, the expression for saving the session file is
53 |
54 | ```APL
55 | {2⎕NQ⎕SE'FileWrite'⊣⎕SE⎕WS'File'⍵}
56 | ```
57 |
58 | which can also be [found on APLcart](https://aplcart.info/?q=save%20session%20configuration#).
59 |
60 | Of course, you might instead define `rep` in the root namespace when starting an exploratory coding session, for convenience.
61 |
62 | !!! Warning
63 | Remember that others running your code base might not have comparable environments to that in which which you developed the code. Best practice is to ensure that all necessary code and data is contained locally within your application.
64 |
65 | Your organisation might also have rules relating to the types and locations of custom items that prevent you from using such techniques.
66 |
67 | ## Code in component files
68 | The **object representation** `⎕OR` of an APL function or operator can be used to store code in component files. These can then be fixed (defined) with `⎕FX` upon loading.
69 |
70 | ## Source code in text files
71 | Until recently, while it was possible to print source code as text for use in articles and tutorials, generally code was distributed in binary workspaces. The exact format and file extension varies between [APL implementations](https://aplwiki.com/wiki/List_of_language_developers), but for Dyalog these are **.dws** files.
72 |
73 | Nowadays, Unicode has enabled the widespread ability to represent characters other than just ASCII. Source code management systems, such as Git and SVN, have encouraged the use of Unicode text files as source code representation.
74 |
75 | Dyalog provides mechanisms for importing text source into the active workspace, and vice versa, including the ability to associate text files with names in the workspace such that changing one will affect the other.
76 |
77 | ### SALT
78 | The **S**imple **A**PL **L**ibrary **T**oolkit allows you to store APL source code as text files with the extension **.dyalog**
79 |
80 | ```APL
81 | Foo←{3×⍵}
82 | ]save Foo /tmp/Foo
83 | \tmp\Foo.dyalog
84 |
85 | ]load /tmp/Foo
86 | Foo
87 | ```
88 |
89 | If the full file path is not specified, SALT will look in a configurable collection of folders on the file system for Dyalog source files. The source folders can be viewed and configured in **Options → Configure → SALT**.
90 |
91 | Read [Chapter R of Mastering Dyalog APL](https://www.dyalog.com/uploads/documents/MasteringDyalogAPL.pdf#%5B%7B%22num%22%3A1305%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C175%2C640%2C0%5D) and the
92 | [SALT User Guide](https://docs.dyalog.com/latest/SALT%20User%20Guide.pdf).
93 |
94 | ### ⎕FX
95 | The `⎕FX` system function can be used to define functions or operators from various forms of source code.
96 |
97 | ### ⎕FIX
98 | The `⎕FIX` system function can be used to define various APL items (functions, operators, namespaces etc.) from scripts.
99 |
100 | If `2⎕FIX` is used and changes are made to the APL name using the Dyalog editor, the system will give you the option to save those changes to the text source file at the same time.
101 |
102 | If changes are made to the text file outside of Dyalog, using a standard text editor, then opening that name for editing within Dyalog will give the option to update the code in the active workspace using the modified external source.
103 |
104 | Using [HttpCommand](../Data/#downloading-data-from-the-internet) together with `⎕FIX` is a way to import code from the internet.
105 |
106 | ### Link
107 | For newer applications, we encourage you to try using the [Link](https://github.com/dyalog/link) system which supersedes [SALT](#salt) and associates namespaces in the active workspace with folders in the file system. Using Link, you do not have to remember to `)SAVE` your workspace, changes in the workspace are automatically reflected on the file system.
108 |
109 | ## ⎕CMD ⎕SH
110 | These two functions are used to run command prompt commands (Microsoft Windows) and shell commands (Linux/macOS). For example, try `⎕CMD'whoami'`.
111 |
112 | !!! Warning
113 | You should take caution when using `⎕SH`, as a non-zero exit code from the command line can cause a `DOMAIN ERROR` and the system error code to be lost.
114 |
⎕←r←⎕SH'exit 3'
115 | DOMAIN ERROR: Command interpreter returned failure code 3
116 | ⎕←r←⎕SH'exit 3'
117 | ∧
118 | r
119 | VALUE ERROR: Undefined name: r
120 | r
121 | ∧
122 | See the [documentation for ⎕SH](http://help.dyalog.com/latest/#UNIX_IUG/Calling UNIX commands.htm) for more information.
123 |
124 | While these are quick-and-easy functions to use for those familiar with the command lines on particular operating systems, on some systems they can be slower than the more integrated alternatives. For example, reading a file using `⎕SH` can be significantly slower than using `⎕N...` system functions on some machines.
125 |
126 | Checking if a file exists:
127 | ```
128 | ⍎⊃⎕SH'if test -f /tmp/text; then echo 1; else echo 0; fi'
129 | 1
130 | ⎕NEXISTS'/tmp/text'
131 | 1
132 | ```
133 |
134 | Reading a text file:
135 |
136 | ```
137 | ⍴↑':'(≠⊆⊢)¨⎕sh'cat /etc/passwd'
138 | 44 7
139 | ⍴↑':'(≠⊆⊢)¨(⎕ucs 10 13)((~∊⍨)⊆⊢)⊃⎕nget'/etc/passwd'
140 | 44 7
141 | ⍴⎕CSV⍠'Separator' ':'⊢'/etc/passwd'
142 | 44 7
143 | ```
144 |
145 | Listing the contents of a directory:
146 | ```
147 | ⍴↑⎕sh'ls -l /tmp'
148 | 14 127
149 | ⍴↑⊃0⎕NINFO⍠1⊢'/tmp/*'
150 | 19 85
151 | ```
152 |
153 | Searching within a file:
154 |
155 | ```
156 | ↑':'(≠⊆⊢)¨⎕SH'awk -F'';'' ''$1 ~ /games/ { print $0 }'' /etc/passwd'
157 | ┌─────┬─┬─┬──┬─────┬──────────┬─────────────────┐
158 | │games│x│5│60│games│/usr/games│/usr/sbin/nologin│
159 | └─────┴─┴─┴──┴─────┴──────────┴─────────────────┘
160 |
161 | {⍵⌷⍨⊂⍸'games'∘≡¨⍵[;1]}⎕CSV⍠'Separator' ':'⊢'/etc/passwd'
162 | ┌─────┬─┬─┬──┬─────┬──────────┬─────────────────┐
163 | │games│x│5│60│games│/usr/games│/usr/sbin/nologin│
164 | └─────┴─┴─┴──┴─────┴──────────┴─────────────────┘
165 | ```
--------------------------------------------------------------------------------
/docs/Quirks.md:
--------------------------------------------------------------------------------
1 | # Out in the wild
2 |
3 | Much of what is presented in this course is what is called *modern APL* in that it contains extensions to the [original mathematical notation](https://aplwiki.com/wiki/Timeline_of_influential_array_languages). The term "modern APL" generally means APL implementations with some form of general nested arrays.
4 |
5 | Dyalog maintains long-term backwards compatibility, meaning that code which ran on Dyalog version 1 can be run on the latest version with little or no modification. Therefore it is good to be aware of all of the language constructs, even if some of them have fallen out of fashion in newly-written code.
6 |
7 | ## Branch
8 | Despite long and widespread use in many programming languages, `:If :Else`-style control structures are a relatively recent introduction to some APLs. Early on, the only way to control the flow of execution in APL was to using the branching arrow `→`.
9 |
10 | ```APL
11 | →ln ⍝ Go to integer line number ln or label ln:
12 | →0 ⍝ Exit current function and resume calling line
13 | →⎕LC ⍝ Resume suspended execution
14 | → ⍝ Clear one stack suspension
15 | →condition/ln ⍝ If condition is true (1), go to line ln, otherwise go to next line
16 | ```
17 | ```
18 | ```
19 | ---
20 | ```APL
21 | ∇ r←BFac n ⍝ Branching Factorial
22 | [1] ⍝ {⍵=1:⍵ ⋄ ⍵×∇ ⍵-1}
23 | [2] →(n=1)/4
24 | [3] r←n×BFac n-1 ⋄ →0
25 | [4] r←n
26 | ∇
27 | ```
28 | ```
29 | ```
30 |
31 | Keeping track of line numbers in this way would be a hassle for large programs. The introduction of *labels* makes understanding code easier.
32 |
33 | ```APL
34 | ∇ r←BFacL n ⍝ Branching Factorial
35 | [1] ⍝ with Label
36 | [2] ⍝ {⍵=1:⍵ ⋄ ⍵×∇ ⍵-1}
37 | [3] →(n=1)/end
38 | [4] r←n×BFac n-1 ⋄ →0
39 | [5] end:r←n
40 | ∇
41 | ```
42 |
43 | The use of `:GoTo` might be more suggestive to those unfamiliar with `→`.
44 |
45 | ```APL
46 | ∇ r←BFacG n ⍝ Branching Factorial
47 | [1] ⍝ with GoTo
48 | [2] ⍝ {⍵=1:⍵ ⋄ ⍵×∇ ⍵-1}
49 | [3] :If n=1
50 | [4] :GoTo end
51 | [5] :EndIf
52 | [6] r←n×BFac n-1
53 | [7] :Return
54 | [8] end:r←n
55 | ∇
56 | ```
57 |
58 | You might prefer to use conditional keywords to keep blocks of statements together in a predictable way.
59 |
60 | ```APL
61 | ∇ r←BFacI n ⍝ Branching Factorial
62 | [1] ⍝ with If
63 | [2] ⍝ {⍵=1:⍵ ⋄ ⍵×∇ ⍵-1}
64 | [3] :If n=1
65 | [4] r←n
66 | [5] :Else
67 | [6] r←n×BFac n-1
68 | [7] :EndIf
69 | ∇
70 | ```
71 |
72 | ## The axis operator
73 | Before the rank operator was invented[1](#hopl4), certain functions had comparable behaviour when used in conjunction with *the axis operator*. However, the axis operator is not a true operator: it is not general, does not fit the standard function-operator syntax and does not work with user-defined functions.
74 |
75 | However, there are some useful applications of the axis operator which are handy to know. Some can be replicated with combinations of both `⍤` rank and `⍉` transpose, for example.
76 |
77 | ```APL
78 | A←2 3 4⍴⎕A
79 | ,[1 2]A ⍝ Merge first two axes
80 | ,[2 3]A ⍝ Merge last two axes
81 |
82 | (2 3⍴⍳6),[1]2 3⍴⎕A ⍝ Catenate first axis
83 | (2 3⍴⍳6),2 3⍴⎕A ⍝ Catenate last axis
84 |
85 | (2 3⍴⍳6),[0.5]2 3⍴⎕A ⍝ Laminate before 1st axis
86 | (2 3⍴⍳6),[2.5]2 3⍴⎕A ⍝ Laminate after 2nd axis
87 |
88 | ⊂[1 3]A ⍝ Enclose matrices along the first and third axes
89 | ```
90 |
91 | As an exercise, try to reformulate the expressions above using only combinations of the operand functions enclose (`⊂⍵`), ravel (`,⍵`) or catenate-first (`⍪⍵`); the rank operator (`F⍤k`); and dyadic transpose (`⍺⍉⍵`).
92 |
93 | ??? Example "Answers"
94 | A←2 3 4⍴⎕A
95 | ⍉,⍤2⊢2 3 1⍉A ⍝ Transpose and ravel matrices
96 | ,⍤2⊢A ⍝ Ravel matrices
97 |
98 | (2 3⍴⍳6)⍪2 3⍴⎕A ⍝ Catenate first axis
99 | (2 3⍴⍳6)(⍪⍤1)2 3⍴⎕A ⍝ Catenate vectors
100 |
101 | (2 3⍴⍳6)(2 3 1⍉,⍤0⍤1)2 3⍴⎕A ⍝ Laminate scalars within each pair of rows and transpose the result
102 | (2 3⍴⍳6)(,⍤0⍤1)2 3⍴⎕A ⍝ Laminate scalars within each pair of rows
103 |
104 | ⊂⍤2⊢3 1 2⍉A ⍝ Enclose matrices after transpose
105 |
106 | Knowledge of the axis operator is required for anyone maintaining code which uses it, and it should be used to retain style unless all uses are to be replaced systematically with the rank operator. In any case, some people feel it provides pleasant [syntactic sugar](https://www.quora.com/What-is-syntactic-sugar-in-programming-languages) over the equivalent expressions which use rank and transpose.
107 |
108 | ## Portable defaults
109 | All [system functions and variables](../Quad names/) have default values. However, to guarantee that your code will run correctly when copied into other users' code bases, it is a good idea to set some of these values at the top level of the namespaces or functions which constitute the entry points of your application.
110 |
111 | It is common to see `(⎕ML ⎕IO)←1` or similar at the top of production functions and scripted namespaces.
112 |
113 | ### Migration level
114 | The system variable `⎕ML` ("*quad-em-ell*") specifies a "migration level" in order to allow code bases from other APL systems to work unmodified in Dyalog. In particular, some primitive symbols (like `⊃` and `↑`) have different definitions depending on the migration level.
115 |
116 | By default, this is set to 1.
117 |
118 | ### Index origin
119 | Due to its origin as a notational tool for teaching, arrays are indexed starting from 1 by default. However, some users are accustomed or find it convenient for indexing to start from 0 instead. In this way, it can be considered an "offset from the beginning of the array" rather than an [ordinal](https://en.wikipedia.org/wiki/Ordinal_numeral) index.
120 |
121 | Dyalog provides a way to choose whether arrays are indexed starting from zero or one:
122 |
123 | ```APL
124 | 'ABCD'[1]
125 | A
126 | ⎕IO←0 ⍝ Quad eye-oh gets zero
127 | 'ABCD'[1]
128 | B
129 | ```
130 |
131 | APLers all agree that it would be better if there was only one option for `⎕IO`, but none of them can agree on what it should be.
132 |
133 | The author is sure that saying "the zero-th element" is incorrect. The *first* element may be labelled 0, but it is still the first element.
134 |
135 | ## Caution with namespace references
136 | It is a good idea to organise code bases into namespaces which each serve a particular purpose. In this case, you will likely want to somehow access names in one namespace from calling functions in a different namespace. There are ways to do this, but they should all be used with caution.
137 |
138 | `⎕CS` is a system function to change the current namespace.
139 |
140 | You can set the local search path for names using `⎕PATH`. For example, if you have a collection of utilities in `#.utils`, you do not need to keep referring to those functions by their full paths `#.utils.Foo` `#.utils.Goo` if you set `⎕PATH←'#.utils'`.
141 |
142 | `:With` was really designed when working with GUI objects:
143 |
144 | ```APL
145 | ∇ make caption
146 | [1] :With 'MyForm' ⎕WC 'Form'
147 | [2] Caption←caption
148 | [3] Coord←'Pixel'
149 | [4] Size←400 800 ⋄ Posn←20 30
150 | [5] onClose←'HandleClose'
151 | ...
152 | ```
153 |
154 | However, some users think that `⎕PATH`, `:With` and `⎕CS` simply make applications more difficult to debug when something goes wrong. In large functions, debugging can become difficult if namespaces and search paths are altered far from where an error occurs. This is especially problematic in tradfns with dynamic scope, if you forget to localise `⎕PATH` within a function.
155 |
156 | One recommendation is to assign a reference to a long namespace path at the top of a function or namespace:
157 |
158 | ```APL
159 | str←#.utils.strings
160 | str.(nl split) char_vec_with_embedded_newlines
161 | ```
162 |
163 | ## Auxiliary processors
164 | **Auxiliary Processors** AKA [**APs**](https://help.dyalog.com/17.1/index.htm#UserGuide/Installation and Configuration/Auxiliary Processors.htm) are a legacy mechanism akin to [using compiled shared native libraries with ⎕NA](../Interfaces/#name-association). We do not recommended using them in new projects, but they remain for the support of existing systems.
165 |
166 | They were generally used when equivalent functionality written in APL was not performant, or not possible at the time. For example, in the past, set functions such as *Index-Of* `⍺⍳⍵` and *Membership* `⍺∊⍵` were slow. For some time, APs had the general advantage that the interpreter would not crash due to an error in an AP, but these days their performance is relatively poor due to having to copy data in and out of the active workspace. The performance of primitive functions is much better now than in the past and APs are a deprecated feature.
167 |
168 | For some applications, you might want to see the documentation archive for [**information about the XUtils AP**](http://docs.dyalog.com/15.0/Code%20Libraries%20Reference%20Guide.pdf#%5B%7B%22num%22%3A43%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C87%2C707%2C0%5D).
169 |
170 | ## Underscored alphabet
171 | [**See the online documentation for the underscored alphabet**](https://help.dyalog.com/17.1/#Language/System%20Functions/Underscored%20Alphabetic%20Characters.htm)
172 |
173 | Before Unicode... before personal computers... before [software fonts](https://en.wikipedia.org/wiki/Computer_font)... before [electronic hardware fonts](https://en.wikipedia.org/wiki/Mullard_SAA5050)... was... the typewriter! Due to its age, the history of APL is enough to appreciate many of the modern features that we take for granted in computing.
174 |
175 | The first APL ([Iverson Notation](https://aplwiki.com/wiki/Iverson_notation)) was written by hand. Some of the earliest APLs on computers were on mainframe computers and accessed via teletype terminals such as the [IBM Selectric typewriter](https://en.wikipedia.org/wiki/IBM_Selectric_typewriter). These typewriters had type balls (colloquially referred to as "golf balls") which could be swapped out to enable different fonts - including an APL font.
176 |
177 | It is quite interesting to see such a system in action, for example in this [demonstration of APL from 1975](https://youtu.be/_DTpQ4Kk2wA), you can hear the typewriter aggressively rattling off keystrokes as the result of computation is relayed back to the terminal.
178 |
179 | While all of this is now historical curiosity, one hangover might be relevant if you are working on older code bases. Older APL systems did not have lower case letters. Instead, there was the alphabet (`⎕A`), and an underscored alphabet (`⎕Ⓐ` - although in some fonts this is rendered as a circled-A Ⓐ). In recent versions of Dyalog, some accented characters have been co-opted for compatibility with older applications.
180 |
181 | ## Refactoring
182 | It is usually best to continue code in the style in which you find it. This can include continuing to use many of the constructs which are here presented as "historical quirks". However, if an important part of the code base has become very difficult to reason about and debug, it might be worth your time to refactor it using more modern constructs and practices. This is referred to as "paying down technical debt", as the debt is accrued when the original author wrote code which makes it difficult, and therefore takes more time and money, to maintain. After paying the technical debt, it is hoped that future maintenance is far less resource intensive - so it is always a tradeoff between spending time now to make code nicer for the future, or spending time later wondering how the thing even works.
183 |
184 | ---
185 |
186 | 1. Hui, R.K. and Kromberg, M.J., 2020. APL Since 1978. Proceedings of the ACM on Programming Languages. **Section 3.1**
--------------------------------------------------------------------------------
/docs/finding-and-replacing-values.md:
--------------------------------------------------------------------------------
1 | # Finding and Replacing Values
2 |
3 | ## Searching and finding
4 | It is common to make multiple equality comparisons. Doing this one at a time becomes tedious.
5 |
6 | ```APL
7 | text ← 'This is my sentence'
8 | (text='a')∨(text='e')∨(text='i')∨(text='o')∨(text='u')
9 | ```
10 | ```
11 | 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1
12 | ```
13 |
14 | The membership function returns a Boolean array (`1`s and `0`s) of the same shape as `⍺` where `1` indicates the location of any of the elements in `⍵`.
15 |
16 | ```APL
17 | text∊'aeiou'
18 | ```
19 | ```
20 | 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1
21 | ```
22 | ---
23 | ```APL
24 | text ← 2 6⍴'I LIKE APL '
25 | text∊'AEIOU'
26 | ```
27 | ```
28 | 1 0 0 1 0 1
29 | 0 1 0 0 0 0
30 | ```
31 |
32 | Find `⍺⍷⍵` will give a `1` indicating the location of the first element of `⍺` when the entire array `⍺` is found as a subarray in `⍵`.
33 |
34 | ```APL
35 | y ← 2 10⍴'APPLESLESSMOTIONSLOW'
36 | 'LESS'⍷y
37 | ```
38 | ```
39 | 0 0 0 0 0 0 1 0 0 0
40 | 0 0 0 0 0 0 0 0 0 0
41 | ```
42 | ---
43 | ```APL
44 | 'LESION'⍷y
45 | ```
46 | ```
47 | 0 0 0 0 0 0 0 0 0 0
48 | 0 0 0 0 0 0 0 0 0 0
49 | ```
50 | ---
51 | ```APL
52 | (2 3⍴'LESION')⍷y
53 | ```
54 | ```
55 | 0 0 0 1 0 0 0 0 0 0
56 | 0 0 0 0 0 0 0 0 0 0
57 | ```
58 |
59 | Index of `⍺⍳⍵` will return the index in `⍺` where `⍵` is found as a major cell.
60 |
61 | ```APL
62 | text ← 2 3 4⍴'SOME APPLES'
63 | text∊'LESS'
64 | ```
65 | ```
66 | 1 0 0 1
67 | 0 0 0 0
68 | 1 1 1 1
69 |
70 | 0 0 1 0
71 | 0 0 0 1
72 | 1 1 1 0
73 | ```
74 | ---
75 | ```APL
76 | 'LESS'⍷text
77 | ```
78 | ```
79 | 0 0 0 0
80 | 0 0 0 0
81 | 1 0 0 0
82 |
83 | 0 0 0 0
84 | 0 0 0 0
85 | 0 0 0 0
86 | ```
87 | ---
88 | ```APL
89 | (1⌷text)⍳'LESS'
90 | ```
91 | ```
92 | 3
93 | ```
94 |
95 | For subarrays not found in `⍺`, index-of returns `1+≢⍺`.
96 |
97 | ```APL
98 | 'keep'⍳'qwert'
99 | 5 5 2 5 5
100 | ```
101 |
102 | ## Sorting and grouping
103 | To sort, index by the grade.
104 |
105 | ```APL
106 | Sort←{(⊂⍋⍵)⌷⍵}
107 | Sort 'the alphabet'
108 | ```
109 | ```
110 | aabeehhlptt
111 | ```
112 |
113 | Grouping is an incredibly common operation when handling data. The python "dataframe" framework Pandas [has a groupby function](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.groupby.html) and anybody who has used SQL [is likely to be familiar with this idea](https://www.w3schools.com/sql/sql_groupby.asp).
114 |
115 | The key operator applies its operand function to groups of [major cells](./cells-and-axes.md) corresponding to the unique major cells in `⍵`. For a vector, this is the unique list of elements.
116 |
117 | In the monadic case, `⍵` is a list of indices.
118 |
119 | ```APL
120 | {⍺,⊂⍵}⌸'mississippi'
121 | ┌─┬────────┐
122 | │m│1 │
123 | ├─┼────────┤
124 | │i│2 5 8 11│
125 | ├─┼────────┤
126 | │s│3 4 6 7 │
127 | ├─┼────────┤
128 | │p│9 10 │
129 | └─┴────────┘
130 | ```
131 |
132 | In the dyadic case, it is a list of keys which are provided as `⍺`.
133 |
134 | ```APL
135 | ↑'ABBDDDCDBAA' 'mississippi'
136 | ABBDDDCDBAA
137 | mississippi
138 |
139 | 'ABBDDDCDBAA'{⍺,⊂⍵}⌸'mississippi'
140 | ┌─┬────┐
141 | │A│mpi │
142 | ├─┼────┤
143 | │B│isp │
144 | ├─┼────┤
145 | │D│sisi│
146 | ├─┼────┤
147 | │C│s │
148 | └─┴────┘
149 | ```
150 |
151 | Interval index is a function for classifying data by boundaries.
152 |
153 | See if you can write the `Grade` function from [problem set 3, problem 6](./array-logic-data-driven-conditionals.md#problem-set-3) using **interval index** `⍺⍸⍵`.
154 |
155 | ???Example "Answer"
156 |
157 | ```APL
158 | Grade ← {'FDCBA'[0 65 70 80 90⍸⍵]}
159 | ```
160 |
161 | ## Set functions
162 | Those familiar with set theory from traditional mathematics will recognise the following symbols. The APL functions are closely related to their set theory counterparts.
163 |
164 | The union of two vectors contains elements from `⍺` catenated with elements in `⍵` not found in `⍺`.
165 |
166 | ```APL
167 | 'WASH' ∪ 'SHOUT'
168 | WASHOUT
169 | ```
170 |
171 | The intersection contains elements only found in both.
172 |
173 | ```APL
174 | 'WASH' ∩ 'SHOUT'
175 | SH
176 | ```
177 |
178 | The function without removes elements from `⍺` which are found in `⍵`.
179 |
180 | The set difference is then neatly expressed as [a fork](./user-defined-functions.md#the-three-function-styles) of the union without the intersection.
181 |
182 | ```APL
183 | 'WASH' (∪~∩) 'SHOUT'
184 | WAOUT
185 | ```
186 |
187 | ## Assigning to arrays
188 |
189 | ### Indexed Assignment
190 | Assign values at specified indices.
191 |
192 | ```APL
193 | t←4 4⍴'some sample text'
194 | t[⍸t∊'aeiou']←'!'
195 | ```
196 |
197 | ### Selective Assignment
198 | Most ways of [selecting from arrays](./selecting-from-arrays.md) can be used to change values in an array. Here is an example using **compress**:
199 |
200 | ```APL
201 | a ← 2 3⍴⎕A
202 | (1 0⌿a)←'⌺'
203 | a
204 | ```
205 | ```
206 | ⌺⌺⌺
207 | DEF
208 | ```
209 |
210 | ### Modified Assignment
211 | Experiment with the following expressions, paying particular attention to the `name f← array` construct.
212 |
213 | ```APL
214 | salaries←18250 42500 56000 57250 48640
215 | codes←'ACDDC'
216 | salaries×←1.1
217 | salaries[⍸codes='C']×←1.1
218 |
219 | a←⎕A
220 | (3↑a),←'abc'
221 | (¯4↑a),←'xyz' ⍝ this one will error — think about why!
222 | ```
223 |
224 | ### The At operator
225 | Monadic functions take a single right argument array as input. Dyadic functions take two argument arrays.
226 |
227 | Monadic operators take a single left operand which can be a function or an array (as in `+/` where plus `+` is the function operand and reduce `/` is the operator).
228 |
229 | Dyadic operators take two operands which could be functions or arrays depending on the operator's definition. For example, the [rank operator](./cells-and-axes.md#the-rank-operator) `F⍤k` takes a function left operand `F` and array right operand vector `k` of up to 3 elements.
230 |
231 | Selective and indexed assignment methods will change the values of variables. The "at" operator `@` merges two arrays at specified indices and returns a new array.
232 |
233 | If a function right operand returns a boolean array when applied to `⍵` (e.g. `3=1 3 5`) then ones `1` in the boolean array determine where scalars of `⍺` are inserted.
234 |
235 | ```APL
236 | ('∆⍥'@{⍵∊'AEIOU'})2 3⍴'DYALOG'
237 | ```
238 | ```
239 | DY∆
240 | L⍥G
241 | ```
242 | ---
243 | ```APL
244 | (' '@2 3 4)'DYALOG'
245 | ```
246 | ```
247 | D OG
248 | ```
249 | ---
250 | ```APL
251 | (' '@(1 2)(1 3)(2 1))2 3⍴'DYALOG'
252 | ```
253 | ```
254 | D
255 | OG
256 | ```
257 |
258 | Generally, the left operand to `@` is a function applied to scalars in `⍵` which are specified by a right operand that is either an array of scalar (simple or enclosed vector) indices or a boolean array returned by a right operand function. An array left operand is shorthand for a [constant function](https://aplwiki.com/wiki/Constant) that returns the array.
259 |
260 | ```APL
261 | Upper ← 1∘⎕C
262 | {Upper@{¯1⌽' '=⍵}' ',⍵}'my excellent heading'
263 | My Excellent Heading
264 | ```
265 |
266 | ### Strand Assignment
267 | [**Distributed assignment**](http://help.dyalog.com/latest/#Language/Introduction/Namespaces/Distributed Assignment.htm) or **strand assignment** allows multiple names to be defined using a single assignment arrow `←`.
268 |
269 | ```APL
270 | (max min avg)←{(⌈⌿⍵)(⌊⌿⍵)((+⌿÷≢)⍵)}3 1 4 1 5
271 | ```
272 |
273 | !!! Note
274 | Strand assignment does not require names to be parenthesised, but we strongly recommend it for clarity.
275 |
276 | We can assign items in `nest` to the three variables `s←'A'` `v←1 2 3` and `m←3 3⍴⍳9` using a single assignment arrow.
277 |
278 | ```APL
279 | nest←('A'(1 2 3))(3 3⍴⍳9)
280 | ((s v) m)←nest
281 | ```
282 |
283 | !!! Warning
284 | You might have some issues when using inline, modified or strand assignment in dfns. This is by design, but can be a source of confusion.
285 | ```APL
286 | { a←3 ⋄ f←+ ⋄ a f←3 ⋄ a }⍬
287 | 3
288 | a←3 ⋄ f←+ ⋄ a f←3 ⋄ a
289 | 6
290 | ```
291 |
292 | You can get around these problems by writing `∘⊢` to the immediate right of any function involved:
293 | ```APL
294 | { a←3 ⋄ f←+ ⋄ a f∘⊢←3 ⋄ a }⍬
295 | 6
296 | ```
297 |
298 | You might find it best to simply keep the modification explicit:
299 | ```APL
300 | { a←3 ⋄ f←+ ⋄ a←a+3 ⋄ a }⍬
301 | 6
302 | ```
303 |
304 | ## Problem set 6
305 |
306 | 1. Write a function test if there are any vowels `'aeiou'` in text vector `⍵`
307 |
308 | ```APL
309 | AnyVowels 'this text is made of characters'
310 | ```
311 | ```
312 | 1
313 | ```
314 | ---
315 | ```APL
316 | AnyVowels 'bgxkz'
317 | ```
318 | ```
319 | 0
320 | ```
321 |
322 | ???Example "Answer"
323 | We can use membership to see which elements of our argument belong to the set `'aeiou'`. Then we can then ask if there are any `1`s in the Boolean vector:
324 |
325 | ```APL
326 | AnyVowels ← {∨/⍵∊'aeiou'}
327 | ```
328 |
329 | An easy eay to check for any ones in a high rank Boolean array is to use membership:
330 |
331 | ```APL
332 | AnyVowels ← {1∊⍵∊'aeiou'}
333 | ```
334 |
335 | 1. Write a function to count the number of vowels in its character vector argument `⍵`
336 |
337 | ```APL
338 | CountVowels 'this text is made of characters'
339 | ```
340 | ```
341 | 9
342 | ```
343 | ---
344 | ```APL
345 | CountVowels 'we have twelve vowels in this sentence'
346 | ```
347 | ```
348 | 12
349 | ```
350 |
351 | ???Example "Answer"
352 | Counting the `1`s in the Boolean result of membership `⍺∊⍵` counts the vowels.
353 |
354 | ```APL
355 | CountVowels ← {+/⍵∊'aeiou'}
356 | ```
357 |
358 | 1. Write a function `FoundIn` which accepts a nested scalar or vector of character vectors and returns a `1` where each vector contains letters in the simple character vector `⍺`.
359 |
360 | ```APL
361 | 'ei' FoundIn 'Katie' 'Bob' 'Stephen' 'Jessica' 'Andy'
362 | ```
363 | ```
364 | 1 0 1 1 0
365 | ```
366 |
367 | ???Example "Answer"
368 | One solution is to bind `⍺` to the membership function (`∊∘⍺`) to form a monadic function "contains alpha" which can be applied on each vector in our nested argument.
369 |
370 | ```APL
371 | FoundIn ← {∨/¨∊∘⍺¨⍵}
372 | ```
373 |
374 | 1. Write a function `Clean` that changes all non-digits into stars.
375 |
376 | ```APL
377 | Clean 'Easy as 1, 2 and 3'
378 | ```
379 | ```
380 | ********1**2*****3
381 | ```
382 | ---
383 | ```APL
384 | Clean '1000'
385 | ```
386 | ```
387 | 1000
388 | ```
389 | ---
390 | ```APL
391 | Clean 'APL works!'
392 | ```
393 | ```
394 | **********
395 | ```
396 |
397 | ???Example "Answer"
398 | We cannot assign to `⍵` in a dfn, so we must create an intermediate variable name:
399 |
400 | ```APL
401 | Clean ← {
402 | r←⍵ ⋄ d←~⍵∊⎕D
403 | (d/r)←'*'
404 | r
405 | }
406 | ```
407 |
408 | We can provide a function right operand to the **at** operator which checks for non-digits:
409 |
410 | ```APL
411 | Clean ← '*'@(~∊∘⎕D)
412 | ```
413 |
414 | 1. The following expression contains an error:
415 |
416 | ```APL
417 | ('∆⍥'@1)2 3⍴'DYALOG'
418 | ```
419 | ```
420 | LENGTH ERROR
421 | ('∆⍥'@1)2 3⍴'DYALOG'
422 | ∧
423 | ```
424 |
425 | Change the parenthesised function containing `@` so that it gives the following results:
426 |
427 | 1.
428 |
429 | ```
430 | ∆∆∆
431 | LOG
432 | ```
433 |
434 | 1.
435 |
436 | ```
437 | ∆∆∆
438 | ⍥⍥⍥
439 | ```
440 |
441 | ???Example "Answers"
442 |
443 |
444 |
466 |
467 |
468 | 1. Create a function `ReplaceHead` which returns its left argument vector `⍺`, but with the first `⍴⍵` elements replaced with the contents of `⍵`.
469 |
470 | ```APL
471 | 'apple' ReplaceHead 'Eat'
472 | ```
473 | ```
474 | Eatle
475 | ```
476 | ---
477 | ```APL
478 | 'apple' ReplaceHead 'rang'
479 | ```
480 | ```
481 | range
482 | ```
483 | ---
484 | ```APL
485 | 'apple' ReplaceHead 'ENTERPRISE'
486 | ```
487 | ```
488 | ENTER
489 | ```
490 |
491 | ???Example "Answers"
492 | This solution uses indexed assignment:
493 |
494 | ```APL
495 | ReplaceHead ← {r←⍺ ⋄ s←(≢⍺)⌊≢⍵ ⋄ r[⍳s]←s↑⍵ ⋄ r}
496 | ```
497 |
498 | This solution uses the **over** operator `F⍥G` to express the minimum length of `⍺` and `⍵`. It then uses the **at** operator to do the substitution.
499 |
500 | ```APL
501 | ReplaceHead ← {s←⍺⌊⍥≢⍵ ⋄ (s↑⍵)@(⍳s)⊢⍺}
502 | ```
503 |
504 | 1. Bus stops in a town are labelled **A** to **E**. Define a function RouteMatrix which returns a Boolean matrix where `1`s indicate that buses go from one bus stop to the next.
505 |
506 | ```APL
507 | RouteMatrix 'BE' 'C' 'AE' 'BCE' 'A'
508 | ```
509 | ```
510 | 0 0 1 0 1
511 | 1 0 0 1 0
512 | 0 1 0 1 0
513 | 0 0 0 0 0
514 | 1 0 1 1 0
515 | ```
516 | ---
517 | ```APL
518 | 'ABCDE'⍪RouteMatrix 'C' 'CDE' 'ABDE' 'E' 'B'
519 | ```
520 | ```
521 | A B C D E
522 | 0 0 1 0 0
523 | 0 0 1 0 1
524 | 1 1 0 0 0
525 | 0 1 1 0 0
526 | 0 1 1 1 0
527 | ```
528 |
529 | ???Example "Answer"
530 | ```APL
531 | RouteMatrix ← {'ABCDE'∘.∊⍵}
532 | ```
533 |
--------------------------------------------------------------------------------
/docs/Namespaces.md:
--------------------------------------------------------------------------------
1 | # Namespaces and other objects
2 | If you are familiar with **Object Oriented Programming** (**OOP**) concepts and have experience using them from another language, then Dyalog's classes and namespaces should hopefully feel quite straightforward to you.
3 |
4 | *[OOP]: Object-oriented programming
5 |
6 | There is a video [:simple-youtube: Namespaces in Dyalog APL](https://youtu.be/eBEwBSO5mBA) with a basic introduction to the concepts and usage of namespaces.
7 |
8 | Two documents are also worth looking at, whether or not you are familiar with OOP:
9 |
10 | - [Object Oriented Programming for APL Programmers](http://docs.dyalog.com/latest/Object%20Oriented%20Programming%20for%20APL%20Programmers.pdf)
11 | - [A Quick Introduction to Object Oriented Programming for Impatient APL Programmers](http://docs.dyalog.com/latest/Object%20Oriented%20Programming%20for%20Impatient%20APL%20Programmers.pdf)
12 |
13 | If nested arrays are arrays inside arrays; **namespaces** are a bit like a workspace within a workspace. They are **objects** which contain collections of names, and these names can be listed as before, but using the dot `.` syntax from object-oriented programming.
14 |
15 | ```APL
16 | )ns ns ⍝ Create an empty namespace called ns
17 | ns.var←1 2 3 ⍝ Create a variable in ns called var
18 | ns.fn←{⍺,⍵} ⍝ Create a function in ns called fn
19 | ⎕nl-9 ⍝ List the names of objects in the current namespace
20 | ┌──┐
21 | │ns│
22 | └──┘
23 | ns.⎕nl-⍳9 ⍝ List all names in ns
24 | ┌──┬───┐
25 | │fn│var│
26 | └──┴───┘
27 | )cs ns ⍝ Change into ns
28 | ⎕this.⎕nl-⍳9 ⍝ The current namespace is ⎕THIS
29 | ┌──┬───┐
30 | │fn│var│
31 | └──┴───┘
32 | #.⎕nl-⍳9 ⍝ The root namespace is #
33 | ┌──┐
34 | │ns│
35 | └──┘
36 | ```
37 |
38 | ### Mutable objects
39 | Variables are **pass-by-value**. This means that if one name is used to assign another name, changes to the first name are not reflected in the second name.
40 |
41 | ```APL
42 | var1←1 2 3
43 | var2←var1 ⍝ The value of var1 is assigned to var2
44 | var1←var1+6 ⍝ The value of var2 is changed
45 | ⎕←var2 ⍝ var2 retains the previous value
46 | 1 2 3
47 | ```
48 |
49 | Namespaces are objects and are **pass-by-reference**. All names which are assigned a reference (or **pointer**) can be used to refer to the original object.
50 |
51 | ```APL
52 | )ns ns1
53 | #.ns1
54 | ns1.name←'Bob'
55 | ns2←ns1
56 | ns2.name←'Steve'
57 | ⎕←ns1.name
58 | Steve
59 | ```
60 |
61 | !!! Note "Discussion"
62 | 1. Should there be a difference between `(⎕NS⍬)(⎕NS⍬)` and `(2⍴⎕NS⍬)`?
63 | 1. With "by value" semantics?
64 | 1. With "by reference" semantics?
65 | 1. What properties should two namespaces have in order to compare equal? Which of the following scenarios would be faster?
66 | - Using "by value" semantics?
67 | - **By reference:** when they originate from the same call to `⎕NS`?
68 | 1. Can you find a way to achieve "by value" semantics when using namespaces?
69 |
70 | ??? Example "Discussion notes"
71 | 1. Should there be a difference between `(⎕NS⍬)(⎕NS⍬)` and `(2⍴⎕NS⍬)`?
72 | 1. No difference in this case, there are just empty namespaces: `(2⍴⎕NS⍬)≡(⎕NS⍬)(⎕NS⍬)`
73 | 1. Yes. In `(⎕NS⍬)(⎕NS⍬)` we create two entities. In `(2⍴⎕NS⍬)` we create only one.
74 | 1. What properties should two namespaces have in order to compare equal? Which of the following scenarios would be faster?
75 | 1. **By value**: when the two namespace have the exact same content. This is slow as it requires recursively inspecting each namespace for its contents and doing comparisons on every member.
76 | 1. **By reference**: When the two namespaces originate from the same call to `⎕NS`. This is fast as it requires only the comparison of two pointers.
77 | 1. **Clone** (or **make a deep copy of**) a namespace
78 |
ns1←⎕NS⍬
79 | ns1.vec←1 2 3
80 | ns3←⎕NS ns1
81 | ns3.vec[2]←10 ⍝ Only the new copy is changed
82 | ]disp ns1.vec ns3.vec
83 | ┌→────┬──────┐
84 | │7 8 9│7 10 9│
85 | └~───→┴~────→┘
86 |
87 | ## Dyadic execute `⍎`
88 | Namespaces can be used as a simple dictionary structure.
89 |
90 | ```APL
91 | value ← dictionary.name ⍝ Get value
92 | dictionary.name ← value ⍝ Set value
93 | ```
94 |
95 | But how do we do this when the name can vary? Use variables to store the name.
96 |
97 | ```APL
98 | ns←⎕NS⍬
99 | name←'foo'
100 |
101 | ns⍎name,'←1 2 3'
102 | 10×ns.foo
103 | ```
104 | ```
105 | 10 20 30
106 | ```
107 |
108 | !!! Warning
109 | **Beware:** `⍎` is potentially dangerous:
110 |
111 |
ns←⎕NS⍬
112 | name←'⎕OFF⋄' ⍝ !!!
113 | value←1 2 3
114 | ns {⍺.{⍎⍺,'←⍵'}/⍵} name value
115 |
116 | In production, **validate your arguments**.
117 |
118 | ## Modifying namespaces
119 | In some applications, it is useful to keep a namespace as an object that holds the state of something.
120 |
121 | It is reasonable in this case to write a tradfn that uses a namespace reference as both its argument and result. While tradfns can have the side effect of modifying a namespace, we strongly recommend that functions take arguments and return results anyway.
122 |
123 | ```
124 | ∇ nsref ← rate Modify nsref
125 | nsref.var +← rate
126 | ∇
127 | ```
128 |
129 | ## Code organisation
130 | Having modular code organisation is a very sensible idea. However, it is a recent invention relative to APL. In a newer application, it is not hard to imagine a `utils` namespace, a `maths` namespace, an `interface` namespace and so on. In older applications, such compartmentalisation is often achieved using a naming convention. For example:
131 |
132 | ```APL
133 | ⎕NL 3
134 | displayINPUT
135 | displaySHOW
136 | mathsAVG
137 | mathsDET
138 | mathsSTDEV
139 | utilLINES
140 | utilSPLIT
141 | ```
142 |
143 | This type of code organisation is known as a **flat workspace structure**.
144 |
145 | ## Names and references
146 | In Dyalog there are both *named* and *unnamed* namespaces. Considering our use of the word "name" to refer to a token in the workspace, beginning with a letter, that refers to an array or function or operator, this is certainly a little confusing.
147 |
148 | When creating or modifying a namespace, we can assign the namespace to a name which is a namespace reference.
149 |
150 | ```
151 | nsref ← ⎕NS⍬
152 | nsref.var ← 1 2 3
153 | ⎕←nsref
154 | ```
155 |
156 | However, we can also use *dyadic* `⎕NS` to give the namespace a name. This name also becomes the namespace's default [display form](http://help.dyalog.com/latest/#Language/System Functions/df.htm). The default display form of an unnamed namespace is `[Namespace]`.
157 |
158 | If we assign a reference at the same time, we now have a two ways to refer to the namespace. However, notice that the namespace's name (shown by the display form) stays the same throughout as we are referring to the same namespace object.
159 |
160 | ```
161 | nsref ← 'nsname'⎕NS⍬ ⍝ Assigning to the reference is optional
162 | ⎕←nsref ⋄ ⎕←nsname
163 | nsref2 ← nsname
164 | ⎕←nsref2
165 | ```
166 |
167 | ## Display form
168 | The [**display form**](http://help.dyalog.com/latest/#Language/System Functions/df.htm) of an APL array is what is displayed when you execute an expression to display it:
169 | ```APL
170 | x ← 1 2 3
171 | x ⍝ The display form of x (depending on ]box settings)
172 | 1 2 3
173 | ⎕←x ⍝ The display form of x (depending on ]box settings)
174 | 1 2 3
175 | ```
176 |
177 | Namespaces have a default display form.
178 | The display form can be altered with ⎕DF:
179 | ```APL
180 | abc.⎕DF '#.abc' ⋄ abc
181 | #.abc
182 | def.⎕DF 'Hello' ⋄ def
183 | Hello
184 | (abc def).⎕DF ⎕NULL ⋄ abc def ⍝ reset to default
185 | #.[Namespace] #.def
186 | ```
187 |
188 | ## Scripted namespaces
189 | Store the source namespace as a single piece of text:
190 | ```APL
191 | )ed ⍟ns ⍝ equivalent to '⍟'⎕ED'ns'
192 | ```
193 | or
194 | ```APL
195 | ⎕FIX ':Namespace ns' 'var←123' ':EndNamespace'
196 | ns.var
197 | 123
198 | ⎕SRC ns
199 | :Namespace ns var←123 :EndNamespace
200 | ```
201 |
202 | !!!Note
203 | Every time we ”fix”, the namespace is reset per the script. This means that `ns.var←456` would become `123` since the definition `var←123` is found in the script.
204 |
205 | ```APL
206 |
207 | ns.var←456
208 | ⎕SRC ns
209 | :Namespace ns var←123 :EndNamespace
210 | ns.var
211 | 456
212 | ⎕FIX ⎕SRC ns ⍝ similar to editing and fixing
213 | ns.var
214 | 123
215 |
216 | ```
217 |
218 | The table below compares some methods for a namespace in the workspace versus a namespace script with text source.
219 |
220 | | Display Form | Script |
221 | |---|---|
222 | | Set: `⍵.⎕DF` | Set: `⎕FIX source` |
223 | | Get: `⍕⍵` | Get: `⎕SRC ⍵` |
224 | | Reset: `⍵.⎕DF ⎕NULL` | Reset: `⎕FIX ⎕SRC ⍵` |
225 |
226 | ### :Require
227 | It is possible to have a script depend on other scripts. To do so, use the [**:require**](https://help.dyalog.com/latest/index.htm#Language/Object%20Oriented%20Programming/Including%20Script%20Files.htm) keyword.
228 |
229 | ## Problem set 9
230 | 1. Are namespaces created with `)NS` scripted or unscripted?
231 | 1. Use `⎕FIX` to create an unnamed, scripted namespace
232 |
233 | What happens if you try to edit this script using the Dyalog editor?
234 |
235 | 1. Request handler
236 | 1. Create a namespace called `req` containing
237 | - a variable `status` with the value `200`
238 | - a method `Method` which is the function `{4+2×⍵}`
239 | 1. Within the `req` namespace, apply `Method` to `status` and store the result in `status`
240 | 1. Write a function `Into` that copies a workspace into a namespace
241 |
247 |
248 | ??? Hint
249 | See the [documentation for `⎕NS`](https://help.dyalog.com/latest/index.htm#Language/System%20Functions/ns.htm).
250 |
251 | 1. Write a function that swaps the values of two variables, the names of which are given as a 2-element nested vector of character vectors `⍺`.
252 |
271 |
272 | Use one or more of these scalar namespace properties:
273 |
274 | - Name Class (⎕NC) is 9 (ref; non-scalar arrays and non-nss are 2)
275 | - Data Representation (⎕DR) is 326 (pointer)
276 | - Depth (≡) is 0 (simple scalar)
277 | - Allows dot syntax (ns.name)
278 |
279 | 1. Write a function RefMask that returns an array of the same structure as its argument, but with bits indicating the namespace references.
280 |
281 |
293 |
294 | 1. Write a function `Fetch` which takes namespace reference as left argument and a nested vector of character vector keys as right argument and returns the corresponding values from the namespace.
295 |
296 | 1. Write a function `IsRoot`
297 |
313 |
314 | 1. What are our roots?
315 | Write a function FindRoots that takes an arbitrary array of namespaces and finds the root for each namespace.
316 |
317 |
Line ⎕SE.Dyalog.Utils
330 | ⎕SE ⎕SE.Dyalog ⎕SE.Dyalog.Utils
331 |
332 | Line ⎕SE.cbbot.bandsb2.sb.io
333 | ⎕SE ⎕SE.cbbot ⎕SE.cbbot.bandsb2 ⎕SE.cbbot.bandsb2.sb ⎕SE.cbbot.bandsb2.sb.io
334 |
335 | 1. Where are my children?
336 | Write a function that lists all the children of a given namespace.
337 |
338 | ??? Hint
339 | Note: `⎕NL` is Name List, not Children List
340 | Plan: You'll have to crawl through the entire workspace
341 | Think: How could namespaces still be out of reach?
342 |
--------------------------------------------------------------------------------
/docs/Interpreter-internals.md:
--------------------------------------------------------------------------------
1 | # Interpreter Internals
2 | Just some of the nitty-gritty under the covers.
3 |
4 | ## Storage
5 | Memory allocated to store an array needs space for:
6 |
7 | - Each element of the array (which could be a reference to another array)
8 | - `8×≢⍴array` bytes for the shape (`4×` in 32-bit)
9 | - 4 bytes for the type/rank
10 |
11 | For a reference, the object requires an 8-byte pointer, plus space for the contents of the object.
12 |
13 | ## Data types
14 | Internally, Dyalog represents data with the following types. As a program runs, occasionally the interpreter will squeeze arrays into the smallest data type that can represent a particular figure, which helps keep memory usage low and may allow the interpreter to use vectorised instructions for certain operations on certain data types.
15 |
16 | ### Character
17 | ```
18 | ⎕DR'APL' ⍝ 1-byte
19 | 80
20 | ⎕DR'配列' ⍝ 2-byte
21 | 160
22 | ⎕DR⎕←⎕UCS 128077 ⍝ 4-byte
23 | 👍
24 | 320
25 | ```
26 |
27 | ### Number
28 | ```
29 | ⎕DR 1 0 1 0 1 0 ⍝ 1 bit
30 | 11
31 | ⎕DR 42 ⍝ 1 byte
32 | 83
33 | ⎕DR 128 ⍝ 2 byte
34 | 163
35 | ⎕DR 2*15 ⍝ 4 byte
36 | 323
37 | ⎕DR 0J1 ⍝ Complex (2×8 byte double)
38 | 1289
39 | ```
40 |
41 | #### Floating-point representation
42 | There are also 16-byte decimal floating point numbers available, but you need to enable them with `⎕FR`.
43 |
44 | ```
45 | ⎕PP←34
46 | ○1
47 | 3.141592653589793
48 | ⎕FR←645 ⍝ 64-bit float (default)
49 | ○1
50 | 3.141592653589793
51 | ⎕FR←1287 ⍝ 128-bit decimal
52 | ○1
53 | 3.141592653589793238462643383279503
54 | ```
55 |
56 | #### Comparison tolerance
57 | APL systems prefer to act like traditional arithmetic where possible. However, the base-2 (binary) representation used by computers is unable to represent certain decimal numbers precisely. Therefore, floating point arithmetic voids certain mathematical properties:
58 |
59 | ${{1}\over{3}} = 3 \times {{5}\over{9}} \div 5$
60 |
61 | ```APL
62 | ⎕CT←1e¯14 ⍝ Default comparison tolerance
63 | (1÷3)=3×(5÷9)÷5
64 | ```
65 | ```
66 | 1
67 | ```
68 | ---
69 | ```APL
70 | ⎕CT←0 ⍝ No comparison tolerance
71 | (1÷3)=3×(5÷9)÷5
72 | ```
73 | ```
74 | 0
75 | ```
76 | ---
77 | ```APL
78 | ⎕FR←645 ⋄ ⎕CT←1E¯14
79 | {↑⍵(1=1+10*-⍵)}⍳16
80 | ```
81 | ```
82 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
83 | 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
84 | ```
85 | ---
86 | ```APL
87 | ⎕FR←1287 ⋄ ⎕←⎕DCT
88 | ```
89 | ```
90 | 1E¯28
91 | ```
92 | ---
93 | ```APL
94 | {↑⍵(1=1+10*-⍵)}⍳30
95 | ```
96 | ```
97 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
98 | 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
99 | ```
100 |
101 | For exact comparisons set `⎕CT` to `0`, but beware:
102 |
103 | ```APL
104 | 10=+/100⍴0.1
105 | ```
106 | ```
107 | 1
108 | ```
109 | ---
110 | ```APL
111 | ⎕CT←0
112 | 10=+/100⍴0.1
113 | ```
114 | ```
115 | 0
116 | ```
117 | ---
118 | ```APL
119 | 10-+/100⍴0.1
120 | ```
121 | ```
122 | 1.953992523E¯14
123 | ```
124 |
125 | ### Value types
126 | If more than one name points to the same array value:
127 | ```
128 | a←b←1 2 3 4
129 | ```
130 | then there is a reference count internally, and only one copy of the data is stored in memory.
131 |
132 | If one of those arrays is modified:
133 | ```
134 | b[2]←99
135 | ```
136 | then a copy of the data is taken at that point so that other arrays are unaffected:
137 | ```
138 | a b
139 | ┌───────┬────────┐
140 | │1 2 3 4│1 99 3 4│
141 | └───────┴────────┘
142 | ```
143 |
144 | For a nested array, however, the elements are separate.
145 | ```
146 | a←b←(1 2 3)(4 5)
147 | ```
148 | if one of the elements is modified
149 | ```
150 | b[⊂2 1]←99
151 | ```
152 | then the other elements remain shared between names. In this example, the `1 2 3` array remains shared.
153 |
154 | ### Reference types
155 | While APL arrays are pass-by-value, namespaces and other objects are pass-by-reference.
156 |
157 | ```
158 | nsref←⎕NS⍬
159 | jsonref←⎕JSON'{}'
160 | ⎕FIX':Class myclass' ':Endclass'
161 | ⎕DR¨nsref jsonref myclass
162 | 326 326 326
163 | ```
164 |
165 | Although namespaces are pass-by-reference, the references themselves are still values:
166 | ```
167 | (ns1 ns2)←⎕NS¨2⍴⊂⍬
168 | (ns1 ns2).var←42 99
169 | a←ns1
170 | a.var
171 | 42
172 | a←ns2
173 | a.var
174 | 99
175 | ```
176 |
177 | ## Configuration parameters
178 | Many aspects of the interpreter environment can be modified before runtime using [**configuration parameters**](https://help.dyalog.com/latest/index.htm#UserGuide/Installation%20and%20Configuration/Configuration%20Parameters.htm). The values of configuration parameters are determined by a hierarchy of scope on the operating system. On Microsoft Windows, these are often registry settings at the base level. On Unix-like systems, they are environment variables.
179 |
180 | For example, we can set parameters on the command line in a batch file before starting Dyalog. We can even create custom parameters.
181 |
182 | *Save the following as a .bat file and run it on Windows*
183 |
184 | ```text
185 | @SET FOO=bar
186 | @START "Dyalog" "C:\Program Files\Dyalog\Dyalog APL-64 18.0 Unicode\dyalog.exe"
187 | ```
188 |
189 | See the value of a configuration parameter with `⎕NQ`:
190 |
191 | ```APL
192 | ⎕←2⎕NQ'.' 'GetEnvironment' 'FOO'
193 | ```
194 | ```
195 | bar
196 | ```
197 |
198 | You might find it useful to read the [Dyalog for Microsoft Windows Installation and Configuration Guide](http://docs.dyalog.com/17.1/Dyalog%20for%20Microsoft%20Windows%20Installation%20and%20Configuration%20Guide.pdf#%5B%7B%22num%22%3A22%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C63%2C679.5%2C0%5D) and the [Dyalog for UNIX Installation and Configuration Guide](http://docs.dyalog.com/17.1/Dyalog%20for%20UNIX%20Installation%20and%20Configuration%20Guide.pdf#%5B%7B%22num%22%3A21%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C63%2C541.5%2C0%5D).
199 |
200 | Recent versions of Dyalog support universal [configuration files](https://help.dyalog.com/latest/index.htm#UserGuide/Installation%20and%20Configuration/Configuration%20Files.htm) which work across all supported platforms.
201 |
202 | ## What am I running?
203 | Here are some useful code snippets for finding information about the currently running interpreter:
204 |
205 | What version of Dyalog am I running?
206 |
207 | ```APL
208 | '#'⎕WG'APLVersion' ⍝ '#' is '.' in older versions of Dyalog
209 | ```
210 |
211 | For error reports, get the BuildID:
212 |
213 | ```APL
214 | ⎕←2⎕NQ'#' 'GetBuildID'
215 | ```
216 |
217 | Am I a Unicode interpreter?
218 |
219 | ```APL
220 | 80=⎕DR''
221 | ```
222 |
223 | Am I big endian?
224 |
225 | ```APL
226 | ⍬⍴83 ⎕DR 256
227 | ```
228 |
229 | What is my word width?
230 |
231 | ```APL
232 | 32×1+'4'∊⊃#⎕WG'APLVersion' ⍝ 64-bit has 64 in the platform name. Otherwise the interpreter is 32-bit.
233 | ```
234 |
235 | ## Performance
236 | This course is intended to teach not only the symbols, syntax and system interactions of Dyalog APL, but also to try and teach you *the APL way*. This is a combination of array-oriented problem solving and ability to combine primitives to solve all manner of problems using computers.
237 |
238 | There are some inherent benefits to APL, such as the fact that exploring with APL can often lead you to optimal solutions to particular types of problems. Not only this, but the terseness makes it very easy to experiment and play with different approaches to the same problem.
239 |
240 | Besides APL itself, however, there are particular techniques, considerations, and special optimisations within the Dyalog interpreter that you should be aware of in case you find a performance bottleneck in your application and are wondering how to solve it.
241 |
242 | ### Flat array techniques
243 | Nested arrays are a very convenient construct and can be used to write incredibly elegant expressions for certain operations. For example:
244 |
245 | ```APL
246 | ≢¨⊆⍨1 1 1 0 1 1 1 1 0 1 1 0 1 1 1 1 0 1 1 1 1
247 | 3 4 2 4 4
248 | ```
249 |
250 | However, for long arguments this statement involves allocating many potentially disparate regions of memory and chasing pointers. Try to think of an approach which uses only flat arrays.
251 |
252 | ??? Example "Answer"
253 | One solution [from APLcart](https://aplcart.info/?q=lengths%20of%20groups%20of%201s#):
254 |
255 |
(0~⍨¯1-2-/∘⍸1,1,⍨~)
256 |
257 | Of course, for certain arguments simply having more functions can be a performance penalty. Compare `]runtime` of the two expressions with `short←1=?10⍴2` and `long←1=?1e6⍴2`
258 |
259 | #### Peppery code
260 | One of the [**code smells**](https://en.wikipedia.org/wiki/Code_smell) in APL is large amounts of code to achieve something relatively simple. This is usually (but not always!) an indication that the problem can be thought of in a different way to achieve a more elegant and efficient solution.
261 |
262 | Another code smell is when a solution is littered with the each operator `F¨` . These are explicit looping operations and often suggest that the code can be re-written to take advantage of the implicit iteration and potential parallelisation of the core primitives acting on flat arrays.
263 |
264 | ```APL
265 | arg←?5 3⍴10
266 |
267 | ]runtime -c "{⌊0.5+(+⌿÷≢)⍉⍵}arg" "{⌊0.5+(+/⍵)÷⊃⌽⍴⍵}arg" "{⌊0.5+¨(+/¨s)÷c←≢¨s←↓⍵}arg"
268 | ```
269 | ```
270 |
271 | {⌊0.5+(+⌿÷≢)⍉⍵}arg → 1.1E¯6 | 0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
272 | {⌊0.5+(+/⍵)÷⊃⌽⍴⍵}arg → 8.6E¯7 | -25% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
273 | {⌊0.5+¨(+/¨s)÷c←≢¨s←↓⍵}arg → 1.8E¯6 | +60% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
274 | ```
275 |
276 | ### Loop sometimes
277 | Some types of algorithms really aren't amenable to parallelisation and array-oriented techniques. These are usually problems with heavy dependence on intermediate results. Sometimes they can be encoded in a custom scan or reduction, but the overall algorithm isn't able to take advantage of parallelisation that primitive scans and reductions can.
278 |
279 | Another consideration is that sometimes an elegant-looking solution in APL is quite inefficient. Take the prime number filter we saw early on:
280 |
281 |
Primes ← {⍸2=+⌿0=∘.|⍨⍳⍵}
282 |
283 | An alternative coding uses the multiplication table:
284 |
285 |
Primes ← {i~∘.×⍨i←1↓⍳⍵}
286 |
287 | Of course, the outer product `∘.F` indicates that the number of calculations to compute both of these solutions increases with the square of the input size. We say they have a computational complexity "*of order n squared*" or $O(n^2)$ in [big-O notation](https://en.wikipedia.org/wiki/Big_O_notation). This is a very inefficient way to find prime numbers.
288 |
289 | To see discussions around more efficient ways to compute prime numbers in APL, see [the dfns page on prime numbers](https://dfns.dyalog.com/n_pco.htm).
290 |
291 | Put simply, the fastest algorithm in general is the one which performs the fewest computations. Sometimes there are solutions in APL which use a relatively large amount of memory but are fast in time due to optimised primitives. However, sometimes the domain of the problem grows so large that even these solutions are outperformed by a scalar looping solution. When this is the case, if performance is very important for this part of your application, it can be a good idea to search for pre-existing high performance solutions, or to write the solution in a lower level language, and use `⎕NA` to integrate it with your APL code.
292 |
293 | 1. Try to find an optimised expression which uses the rank operator `F⍤k`.
294 | 1. What is the computational complexity of a custom reduction `F/⍵`?
295 | 1. What is the computational complexity of a custom scan?
296 | 1. How do custom scans and reductions compare with primitive scans and reductions for `+ - × ÷`?
297 |
298 | ### Idioms
299 | APL idioms are short expressions to perform certain tasks. [APLcart](https://aplcart.info) is a comprehensive, searchable list of idioms. Some of these include slightly obscure but performant versions of particular tasks, especially those related to [partitioned functions](https://aplcart.info/?q=partitioned#).
300 |
301 | In Dyalog APL, there are also "idioms" which are specially recognised phrases, or combinations of characters, which are treated as individual tokens and executed with special code, rather than parsed symbol-by-symbol and executed function-at-a-time. These offer significant performance improvements for particular tasks, and are all listed:
302 |
303 | - [latest](https://docs.dyalog.com/latest/CheatSheet%20-%20Idioms.pdf)
304 | - [17.1](https://docs.dyalog.com/17.1/CheatSheet%20-%20Idioms.pdf)
305 | - [12.1](https://help.dyalog.com/12.1/index.html?page=html%2Fidiom%20list.htm)
306 |
307 | ### Philosophical tangent
308 | In an ideal world, programmers could focus on writing purely the "most readable" or "most maintainable" versions of their code. Or, in some sense, simply write "whatever feels right" and the interpreter or compiler could analyse the program and determine the optimal. This is part of what compilers hope to achieve. However, even in one the strongest attempts at this so far, Julia, they emphasise that you still need to learn to write "idiomatic Julia" code. This means understanding how the language works both in terms of semantics and which constructs can be written in a particular way to lead to high performance.
309 |
310 | In Dyalog APL, one example of this is in the primitive operators.
311 |
312 | The key operator `F⌸` applies its operand function `F` to grouped major cells of its argument array. An alternative approach is to have a function `{⊂⍵}⌸` perform the grouping first and then to apply using each `F¨{⊂⍵}⌸` . The end result of the computation is the same, but by having an operator, the interpreter implementors can use especially efficient special code for certain operand functions.
313 |
--------------------------------------------------------------------------------
/docs/loops-and-recursion.md:
--------------------------------------------------------------------------------
1 | # Loops and Recursion
2 | Looping is an incredibly basic and fundamental programming construct which you will notice we have barely used at all so far. Or at least, we haven't used many explicit loops.
3 |
4 | !!! Note "Terminology"
5 | The type of looping over items in a collection as provided by *for* and *while* loops is sometimes referred to as **scalar looping**. Other types of looping in APL might, for example, process each row of a matrix in turn but process whole rows at a time. In contrast, [each `¨`](./multidimensional-and-nested-arrays.md#arrays-have-rank-and-depth) is a mechanism for looping over every item of an array (the [scalars](./multidimensional-and-nested-arrays.md#arrays-are-made-of-scalars)); its operand function can see arrays nested within the scalars.
6 |
7 | ## An introduction to an introduction to an introduction to an introduction to an int...
8 |
9 | ```APL
10 | {⍺←1 1 ⋄ ⍵=2:⍺ ⋄ (⍺,(+/¯2↑⍺))∇⍵-1}
11 | ```
12 |
13 | Try the dfn above with various numeric arguments and consider the following questions:
14 |
15 | 1. Which symbol refers to the function itself?
16 | 1. Which symbol separates expressions?
17 | 1. Which part represents a conditional? This is where one part of code executes only if a preceding statement is true.
18 | 1. What is the default left argument? What happens if you call this function dyadically?
19 |
20 | Give the function [an appropriate name](https://en.wikipedia.org/wiki/Fibonacci_number).
21 |
22 | When a function calls itself like this it is called *recursion*. APL tends to rely less on explicit iteration and recursion than most popular programming languages, but it is good to be able to do it when you need to.
23 |
24 | If a function gets stuck in an infinite loop, use `Action → Interrupt` in the menu. You can also use the key combination `Ctrl+Break` to interrupt a running function.
25 |
26 | 1. Write the shortest dfn which causes infinite recursion.
27 |
28 | 1. Write the shortest dfn which causes infinite recursion unless its argument is `0`.
29 |
30 | 1. The factorial function multiplies integers up to `⍵`. Write the factorial function as a **recursive** dfn called `Factorial`. Use the primitive `!⍵` factorial function to check your solution.
31 |
32 | 1. Write an expression for the factorial function as a reduction (an expression which includes `f/` for some function `f`).
33 |
34 | ## A sort of detour
35 | Dyalog's *grade-up* `⍋` and *grade-down* `⍒` functions are able to [sort any array](https://aplwiki.com/wiki/Total_array_ordering). However, it is interesting and useful to look at other approaches to sorting.
36 |
37 | Here is a function 'NSort' for sorting numeric lists.
38 |
39 | ```APL
40 | NSort←{0=⍴⍵:⍬ ⋄ (U/⍵),∇(~U←⍵=⌊/⍵)/⍵}
41 | ```
42 |
43 | Try `NSort` with some numeric arguments. Here it is presented piece-by-piece. For each comment prompt `⍝`, write a brief description of what that part of the function does. The first one has been done for you.
44 |
45 | ```APL
46 | NSort←{
47 | 0=⍴⍵:⍬ ⍝ Reached end of list, return empty numeric vector
48 | ⋄ ⍝
49 | (U/⍵), ⍝
50 | ∇ ⍝
51 | (~U←⍵=⌊/⍵) ⍝
52 | /⍵ ⍝
53 | }
54 | ```
55 |
56 | Below is a function `TSort` for sorting character matrices.
57 |
58 | ```APL
59 | TSort←{⍺[((⍴⍵)[2])S ⍺⍳⍵]}
60 | S←{⍺=0:⍵ ⋄ (⍺-1)S ⍵[⍋⍵[;⍺];]}
61 | ```
62 |
63 | Examine `TSort` and replace `⍺` below with an appropriate left argument to sort the character matrix `WORDS`.
64 |
65 | ```APL
66 | WORDS←↑'DOLPHIN' 'BRACKEN' 'SAUCER' 'MAGNET' 'FLOP'
67 | ⍺ TSort WORDS
68 | BRACKEN
69 | DOLPHIN
70 | FLOP
71 | MAGNET
72 | SAUCER
73 | ```
74 |
75 | What do the following expressions tell you about the `⍋` grade-up and `⍒` grade-down functions on high-rank arrays?
76 |
77 | ```APL
78 | ⍋4 2 2⍴'ABDCAADCBCDECDEF'
79 | 2 1 3 4
80 | ⍒4 2 2⍴'ABDCAADCBCDECDEF'
81 | 4 3 1 2
82 | ```
83 |
84 | ## You have the power
85 | One common type of iteration is to apply the same function again to the result of the previous application. In traditional mathematics, this is expressed with superscript notation:
86 |
87 | $f(x) = 1 + x$ Increment
88 | $p(x,y) = f^y(x)$ Add
89 | $m(x,y) = p^y(x,0)$ Multiply
90 |
91 | We can express this with the **power** operator:
92 |
93 | ```APL
94 | Inc←{1+⍵}
95 | Plus←{(Inc⍣⍺)⍵}
96 | Times←{⍺(Plus⍣⍵)0}
97 | Power←{⍺(Times⍣⍵)1}
98 | ```
99 |
100 | ## Primitive iterations are still loops
101 | You might have noticed this already, but it is important to know that the rank operator `F⍤j k l` is conceptually a loop. It just happens to be that certain operations are parallelisable, and some of those are parallelised within the Dyalog interpreter.
102 |
103 | We will give some details later in [the section on performance](../Interpreter-internals/#performance).
104 |
105 | ## Reduction is a loop
106 | Primitive reductions are often optimised. For example, **plus-reduction** `+/` is able to take advantage of [vector instructions](https://dyalog.tv/Dyalog19/?v=TqmpSP8Knvg) on certain machines and **and-reduction** `∧/` can quit early if a zero is found in the array.
107 |
108 | We can observe differences by writing a custom function and comparing runtimes:
109 | ```
110 | ]runtime +/?1e7⍴0
111 |
112 | * Benchmarking "+/?1e7⍴0"
113 | (ms)
114 | CPU (avg): 94
115 | Elapsed: 99
116 |
117 |
118 |
119 | ]runtime {⍺+⍵}/?1e7⍴0
120 |
121 | * Benchmarking "{⍺+⍵}/?1e7⍴0"
122 | (ms)
123 | CPU (avg): 3688
124 | Elapsed: 3723
125 | ```
126 |
127 | ## Moving windows
128 | **Windowed-reduction** `⍺ F/ ⍵` is an extension to reduction which applies an *F-reduction* `F/` for a function `F` on a sliding window of size `⍺`.
129 |
130 | It is useful to use catenate to display the windowed selection of the array to which the reduction will be applied:
131 | ```APL
132 | 3,/⍳10
133 | ```
134 | ```
135 | ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬──────┐
136 | │1 2 3│2 3 4│3 4 5│4 5 6│5 6 7│6 7 8│7 8 9│8 9 10│
137 | └─────┴─────┴─────┴─────┴─────┴─────┴─────┴──────┘
138 | ```
139 | ---
140 | ```APL
141 | 3+/⍳10
142 | ```
143 | ```
144 | 6 9 12 15 18 21 24 27
145 | ```
146 |
147 | You can think of this as a special case of [stencil code](https://en.wikipedia.org/wiki/Iterative_Stencil_Loops), for which the [primitive operator](https://aplwiki.com/wiki/Primitive_operator) **stencil** `⌺` was added in version 16.0.
148 |
149 | ```APL
150 | 1↓¯1↓{+/⍵}⌺3⊢⍳10
151 | ```
152 | ```
153 | 6 9 12 15 18 21 24 27
154 | ```
155 |
156 | ## Don't forget scan!
157 | **Scan** `F\⍵` is another construct which is simple in a way that misleads you into thinking it is only used for very specific things. It is exactly a reduction `F/` on successive prefixes of `⍵`. The final value is `F/⍵`.
158 |
159 | ```APL
160 | +/⍳10
161 | 55
162 | +\⍳10
163 | 1 3 6 10 15 21 28 36 45 55
164 |
165 | ⌈\2 0 3 ¯1 4 2 6
166 | 2 2 3 3 4 4 6
167 | ```
168 |
169 | However, scan can be used in many scenarios that you might not expect. Scans are often used in high performance expressions of **partitioned-application** functions which nowadays can be implemented with partitioning primitives (`⍺⊂⍵` and `⍺⊆⍵`), nested arrays and the each operator `F¨`.
170 |
171 | For example, one [YouTube video comparing APL and Haskell](https://youtu.be/QtvvQ7MdwKY) demonstrates one of the big differences in the APL approach in contrast to other types of programming.
172 |
173 | ```APL
174 | 4 {1↓∊' '∘,¨⍺↑' '(≠⊆⊢)⍵} 'take the first four words'
175 | ```
176 | ```
177 | take the first four
178 | ```
179 |
180 | ---
181 |
182 | ```APL
183 | 4 {⍵⌿⍨⍺>+\' '=⍵} 'take the first four words'
184 | ```
185 | ```
186 | take the first four
187 | ```
188 |
189 | Despite both having types of function composition, Haskell and other functional programming languages tend to focus on the composition of those functions as the fundamental process to finding and refining solutions. In APL, we are usually most interested in the fundamental data transformation that is occurring, and using APL's inherently parallel, array-at-a-time primitives to achieve that transformation in a way that can tend towards simple and fast code.
190 |
191 | Between integer and boolean arguments alone there are more interesting constructs than can be covered well here. You can find more on this topic by going to the following links:
192 |
193 | - Watch the Dyalog webinar about [Boolean Scans and Reductions](https://www.youtube.com/watch?v=erv_1LxEByk&list=PLA9gQgjzcpKGwmgzlayqzS_z1ThDwFzl4&index=3)
194 | - Experiment with the examples in an [interactive notebook on TryAPL](https://tryapl.org/?notebook=https%3A%2F%2Fgithub.com%2FDyalog%2Fdyalog-jupyter-notebooks%2Fblob%2Fmaster%2FBoolean+Scans+and+Reductions.ipynb)
195 | - You can [view the static notebook online](https://nbviewer.jupyter.org/github/Dyalog/dyalog-jupyter-notebooks/blob/master/Boolean%20Scans%20and%20Reductions.ipynb)
196 |
197 |
198 | ## For and While
199 | The traditional control structures such as *for loops*, *while loops* and *if statements* weren't introduced in Dyalog [until version 8.0 in 1996](https://aplwiki.com/wiki/Dyalog_APL#Versions). Usually, they are only used for program control on the outer levels, or if an algorithm explicitly requires that type of [scalar looping](#).
200 |
201 | The syntax is mentioned in the [section on user-defined functions](./user-defined-functions.md#marking-tests).
202 |
203 | !!! Hint "Performance note"
204 | When constructing loops, think about whether unnecessary computation is being performed.
205 |
206 | For example,
207 |
208 |
219 |
220 | ## Problem set 8
221 |
222 | ### Word Problems
223 | We are going to do some text processing on a dictionary of words.
224 |
225 | If you have access to the internet, paste the following into your session to download a text file dictionary (917kB in size) and store it as a nested vector of character vectors named `words`.
226 |
227 | ```APL
228 | ]Get bit.ly/unixwords
229 | words ← (⎕UCS 10)(≠⊆⊢)unixwords
230 | ```
231 |
232 | If you have the file on your computer (maybe it was given to you on a USB drive, for example) then you can load it into your workspace from disk using the following expressions.
233 |
234 | ```APL
235 | (content encoding newline) ← ⎕NGET'/path/to/words.txt'
236 | words ← (⎕UCS newline) (≠⊆⊢) content
237 | ```
238 |
239 | Now answer the following questions about `words`.
240 |
241 | 1. How many words have at least 3 `'e'`s in them?
242 |
243 | 1. How many words have exactly two consecutive `'e'`s in them?
244 | The first three such words are `Aberdeen` `Abderdeen's` and `Aileen`.
245 |
246 | 1. What is the shortest word with two consecutive `'a'`s?
247 |
248 | 1. What words have three consecutive double letters? For example, `mississippi` does not but `misseetto` does. Misseetto is not a real word.
249 |
250 | A palindrome is the same when reversed. For example, **racecar** is a palindrome but **racecat** is not.
251 |
252 | 1. How many palindromes are there in `words`?
253 |
254 | 1. Which palindrome in `words` is the longest?
255 |
256 | 1. How many words have their letters arranged in alphabetical order?
257 |
258 | ### Bell Numbers
259 | A [Bell number](https://en.wikipedia.org/wiki/Bell_number) counts the possible partitions of a set. The nth Bell number $B_n$ counts the ways you can partition a set of $n$ elements.
260 |
261 | Here we will investigate 3 ways of computing Bell numbers.
262 |
263 | 1. Brute force
264 |
265 | Write a function `BellBrute` which counts all of the unique partitions of length `⍵` by creating partitions as nested arrays.
266 |
267 | ??? Hint
268 | Binary representations of decimal numbers can be used as partition vectors.
269 |
270 | 1. Triangle scheme
271 |
272 | Implement the [triangle scheme](https://en.wikipedia.org/wiki/Bell_number#Triangle_scheme_for_calculations) for calculations.
273 |
274 | 1. Recurrence relation of binomial coefficients
275 | The Bell numbers satisfy a recurrence relation involving binomial coefficients:
276 |
277 | $B_{n+1} = \sum_{k=0}^{n}\binom{n}{k}B_k$
278 |
279 | Implement `BellRecur` using this formula.
280 |
281 | ### Four is Magic
282 | [Rosetta Code](http://rosettacode.org/wiki/Four_is_magic) has the full problem description.
283 |
284 | Start with a number. Spell that number out in words, count the number of letters and say it all aloud. For example, start with 6, print `'Six is three'` and continue with the number 3. Once you reach 4, print `four is magic`.
285 |
286 | ```
287 | FourMagic 11
288 | Eleven is six, six is three, three is five, five is four, four is magic.
289 | ```
290 |
291 | ### Hash counting string
292 | This problem is from [week 102 of the Perl Weekly Challenge](https://theweeklychallenge.org/blog/perl-weekly-challenge-102/).
293 |
294 | Write a monadic function `HashCount` which takes a scalar integer argument and returns a simple character vector where:
295 |
296 | - the string consists only of digits 0-9 and octothorpes AKA hashes, ‘#’
297 | - there are no two consecutive hashes: ‘##’ does not appear in your string
298 | - the last character is a hash
299 | - the number immediately preceding each hash (if it exists) is the position of that hash in the string, with the position being counted up from 1
300 |
301 | ```
302 | HashCount 1
303 | #
304 | HashCount 2
305 | 2#
306 | HashCount 3
307 | #3#
308 | HashCount 10
309 | #3#5#7#10#
310 | HashCount 14
311 | 2#4#6#8#11#14#
312 | ```
313 |
314 | ### Backspace
315 | Write a function `Backspace` which takes a simple numeric vector argument and treats `0`s like backspaces, removing successive numbers to their left unless none remain.
316 |
317 | ```
318 | Backspace 1 2 0
319 | 1
320 | Backspace 1 5 5 0 2 0 0 8
321 | 1 8
322 | ```
323 |
324 | For an extra challenge, modify your function so that it can also accept a character vector where `\b` is treated as a single token and signifies a backspace character.
325 |
--------------------------------------------------------------------------------
/docs/basic-syntax-and-arithmetic.md:
--------------------------------------------------------------------------------
1 | # Basic syntax
2 |
3 | ## Functions and arguments
4 | APL has two-argument, infix functions. These are called dyadic functions.
5 |
6 | ```APL
7 | 3 × 5
8 | ```
9 | ```
10 | 15
11 | ```
12 | ---
13 | ```APL
14 | 3 - 5
15 | ```
16 | ```
17 | ¯2
18 | ```
19 |
20 | Some functions map between elements of their left and right argument arrays. It is easy to add lists of numbers together:
21 | ```APL
22 | 1 2 3 + 4 5 6
23 | ```
24 | ```
25 | 5 7 9
26 | ```
27 |
28 | Negative numbers are written with a high minus `¯` to differentiate between negation (`-3`) and literal negative numbers (`¯3`).
29 | ```APL
30 | 1 2 3 - 1 0 ¯1
31 | ```
32 | ```
33 | 0 2 4
34 | ```
35 |
36 | There are also one-argument, prefix functions. These are called monadic functions.
37 | ```APL
38 | - 5 ¯3 0 ¯4 2
39 | ```
40 | ```
41 | ¯5 3 0 4 ¯2
42 | ```
43 | ---
44 | ```APL
45 | ⌽ 1 2 3 4 5
46 | ```
47 | ```
48 | 5 4 3 2 1
49 | ```
50 |
51 | Some symbols represent both a monadic and a dyadic function, but these are often closely related. As we will see later, even user-defined functions can be monadic, dyadic or even both (ambivalent).
52 |
53 | **:bulb: Try this**: Use these functions monadically and dyadically:
54 |
55 |
68 |
69 | ## Singleton extension
70 | Dyadic functions can map between a single value and an array of values.
71 | ```APL
72 | 3 × 1 10 100
73 | ```
74 | ```
75 | 3 30 300
76 | ```
77 | ---
78 | ```APL
79 | 3 = 1 2 3 4 5
80 | ```
81 | ```
82 | 0 0 1 0 0
83 | ```
84 |
85 | **:bulb: Try this**: Replace the functions in the previous two expressions with:
86 | [`⌈`](# "Max"){ .glyph}
87 | [`⌊`](# "Min"){ .glyph}
88 | [`<`](# "Less than"){ .glyph}
89 |
90 | While experimenting, you may cause a `LENGTH ERROR`:
91 |
92 | ```APL
93 | 1 2+3 4 5
94 | ```
95 | ```
96 | LENGTH ERROR: Mismatched left and right argument shapes
97 | 1 2+3 4 5
98 | ∧
99 | ```
100 |
101 | Functions such as `+ × ⌈` apply between elements of two arrays of the same shape, or between one element and many if one of the arguments is a single value. However, if the arrays are of two different shapes, it is not clear how the function should be applied. Of course, you may want to [apply a function between all combinations of elements of the left and right argument](./array-logic-data-driven-conditionals.md#the-outer-product), but that will be addressed soon enough.
102 |
103 | ## Order of execution
104 | Expressions are executed from right to left.
105 |
106 | ```APL
107 | 10×⍳2+5
108 | ```
109 | ```
110 | 10 20 30 40 50 60 70
111 | ```
112 |
113 | ??? Info "Show me step-by-step"
114 | To start, there is a literal number 5:
115 | ```APL
116 | 5
117 | 5
118 | ```
119 |
120 | Next, there is a plus `+` with a number 2 to its immediate left, so it is evaluated as two plus five:
121 | ```APL
122 | 2+5
123 | 7
124 | ```
125 |
126 | Then the symbol iota `⍳`. To its left is another function, times `×`, not a value. So the function is called monadically. The monadic form of `⍳` is the index generator, which generates an integer array of length defined by its right argument.
127 | ```
128 | ⍳2+5
129 | 1 2 3 4 5 6 7
130 | ```
131 |
132 | Lastly, another dyadic function, we multiply our list by ten:
133 | ```
134 | 10×⍳2+5
135 | 10 20 30 40 50 60 70
136 | ```
137 |
138 | The expression above is "ten *times* the indices from 1 to *two plus five*, or in short: "ten times iota two plus five". We can make it clearer using (superfluous) **parentheses** `()`.
139 | ```APL
140 | 10×(⍳(2+5))
141 | ```
142 | ```
143 | 10 20 30 40 50 60 70
144 | ```
145 |
146 | Of course, we can change the order of execution using different parentheses.
147 |
148 | ```APL
149 | (10×⍳2)+5
150 | ```
151 | ```
152 | 15 25
153 | ```
154 |
155 | ??? Info "Show me step-by-step"
156 | Beginning from the right, there is a literal number 5:
157 | ```APL
158 | (10×⍳2)+5
159 | 5
160 | ```
161 |
162 | Then there is a plus symbol `+`. Before we can decide if it is being called monadically or dyadically, we must look to the left.
163 |
164 | ```APL
165 | )+5
166 | ```
167 |
168 | A right parenthesis. We must evaluate the contents of the parentheses to see if it is a function or a value.
169 |
170 | ```APL
171 | (10×⍳2)
172 | ```
173 |
174 | This expression evaluates to the list `10 20`. Since it is a value, it is used as the left argument to our plus function.
175 |
176 | ```APL
177 | (10×⍳2)+5
178 | (10 20)+5
179 | 15 25
180 | ```
181 |
182 | Infix (dyadic) functions have a **short** *left* scope and **long** *right* scope. This means that they take the result of everything to their right hand side as their right argument.
183 |
184 | If there is one, the left argument is the value to the immediate left.
185 |
186 | However, juxtaposed values form lists before any functions are applied. This is called stranding and lets us write very natural expressions, such as:
187 |
188 | ```APL
189 | 1 2 3 + 4 5 6
190 | 5 7 9
191 | ```
192 |
193 | but this can lead to some surprises if we are not aware:
194 |
195 | ```APL
196 | 2 + 2 2 + 2
197 | ```
198 | ```
199 | 6 6
200 | ```
201 |
202 | ??? Info "Show me step-by-step"
203 | First, there is a literal number 2
204 | ```APL
205 | 2
206 | 2
207 | ```
208 |
209 | Then there is a symbol `+`. What, if any, is the value to its immediate left?
210 | ```APL
211 | 2 2 + 2
212 | ```
213 |
214 | It is a 2-element vector `2 2`. The plus function maps between these elements and the single number on the right:
215 | ```APL
216 | 2 2 + 2
217 | 4 4
218 | ```
219 |
220 | Finally there is another addition. The overall evaluation looks like the following:
221 | ```APL
222 | 2 + 2 2 + 2
223 | 2 + 4 4
224 | 6 6
225 | ```
226 |
227 | ## Comments
228 | Anything after a lamp symbol `⍝` is ignored.
229 |
230 | ```APL
231 | ⍝ nothing happens on this line
232 | 2 × 3 ⍝ 4 5
233 | ```
234 | ```
235 | 6
236 | ```
237 | ---
238 | ```APL
239 | 'A' ⍝ lamp is not an "A"
240 | ```
241 | ```
242 | A
243 | ```
244 |
245 | ## The reduction operator
246 | Adding a list of numbers *could* become very tedious...
247 | ```APL
248 | 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15
249 | ```
250 | ```
251 | 120
252 | ```
253 |
254 | The **reduce** [operator](./Operators.md) `F/` inserts the function `F` to its left between parts of the right argument array.
255 | ```APL
256 | +/1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
257 | ```
258 | ```
259 | 120
260 | ```
261 |
262 | It is called *reduce* because it reduces the number of dimensions of its argument. In the example above, we have a **vector** (1 dimensional, list) argument and return a **scalar** (0 dimensional, single value) result.
263 |
264 | ## The index generator
265 | The index generator `⍳⍵` generates integers up to the integer right argument `⍵`
266 | ```APL
267 | ⍳10
268 | ```
269 | ```
270 | 1 2 3 4 5 6 7 8 9 10
271 | ```
272 |
273 | So we can do an arithmetic sum as follows
274 |
275 | |**Traditional Mathematical Notation (TMN)** | **APL** |
276 | |---|---|
277 | | $\sum_{n=1}^N n$ | `+/⍳N`
278 |
279 | ## What do these errors mean?
280 | While experimenting, you are very likely to come across these:
281 |
282 | ```APL
283 | ⍳¯4
284 | ```
285 | ```
286 | DOMAIN ERROR
287 | ⍳¯4
288 | ∧
289 | ```
290 |
291 | The `DOMAIN ERROR` means that APL cannot compute what you are asking for. In this case, it cannot generate indices up to a negative number. Negative numbers are outside the domain of the index generator function. How might you [generate integers from 1 to negative four](./dfns-and-assignment.md#problem-set-2)?
292 |
293 | ```APL
294 | 1+
295 | SYNTAX ERROR: Missing right argument
296 | 1+
297 | ∧
298 | ```
299 |
300 | A `SYNTAX ERROR` means that the expression which you tried to execute does not make sense. In the case above, it is because functions always either take a single argument to their right or two arguments, one to the right and one to the left. Functions never take a single argument to their left.
301 |
302 | ```APL
303 | a
304 | VALUE ERROR: Undefined name: a
305 | a
306 | ∧
307 | ```
308 |
309 | A `VALUE ERROR` means that there is nothing associated with the name provided. We have not seen any named functions or variables yet; nothing has been assigned to the name `a`, so trying to use it in an expression is meaningless.
310 |
311 | ## Problem Set 1
312 | 1.
313 | The average daily temperatures, in degrees Celcius, for 7 days are stored in a variable `t_allweek`.
314 |
315 | ```APL
316 | t_allweek ← 11.7 8.6 9.7 14.2 6.7 11.8 9.2
317 | ```
318 |
319 | Use APL to compute the follwing:
320 |
321 | 1. The highest daily temperature
322 | 1. The lowest daily temperature
323 | 1. The range of (difference between the largest and the smallest) temperatures
324 | 1. Each temperature rounded to the nearest whole number
325 |
326 | ??? Example "Answers"
327 |
328 |
341 | ```APL
342 | (⌈/t_allweek)-⌊/t_allweek
343 | 7.5
344 | ```
345 |
346 | > You may have found the correct answer using the following expression:
347 | ```APL
348 | ⌈/t_allweek-⌊/t_allweek
349 | 7.5
350 | ```
351 |
352 | > but this is less efficient because it does more subtractions than it needs to. Recall the right-to-left evaluation:
353 | ```APL
354 | ⌈/ t_allweek - ⌊/ t_allweek
355 | ⌈/ t_allweek - 6.7
356 | ⌈/ 11.7 8.6 9.7 14.2 6.7 11.8 9.2 - 6.7
357 | ⌈/ 5 1.9 3 7.5 0 5.1 2.5
358 | 7.5
359 | ```
360 |
361 | > if we use parentheses `()` to force APL to compute the maximum of the list before doing subtraction, we only do a single subtraction instead of 7:
362 | ```APL
363 | ( ⌈/t_allweek ) - ⌊/ t_allweek
364 | ( ⌈/t_allweek ) - 6.7
365 | ( 14.2 ) - 6.7
366 | 7.5
367 | ```
368 |
369 |
370 |
371 | To round to the nearest whole number, either add 0.5 and round down:
372 | ```APL
373 | ⌊0.5+t_allweek
374 | 12 9 10 14 7 12 9
375 | ```
376 |
377 | or subtract 0.5 and round up:
378 | ```APL
379 | ⌈t_allweek-0.5
380 | 12 9 10 14 7 12 9
381 | ```
382 |
383 |
384 |
385 | 1. A Mathematical Notation
386 |
387 | Use APL to evaluate the following
388 |
389 | 1. $\prod_{n=1}^{12} n$ (multiply together the first twelve integers)
390 |
391 | 1. $\sum_{n=1}^{17}n^2$ (add together the first seventeen squared integers)
392 |
393 | 1. $\sum_{n=1}^{100}2n$ (add together the first one hundred positive even integers)
394 |
395 | 1. $\sum_{n=1}^{100}2n-1$ (add together the first one hundred odd integers)
396 |
397 | 1. In TMN, the following expression is equal to `0`, why does the following return `70` in APL?
398 | ```APL
399 | 84 - 12 - 1 - 13 - 28 - 9 - 6 - 15
400 | ```
401 | ```
402 | 70
403 | ```
404 |
405 | *[TMN]: Traditional Mathematical Notation
406 |
407 | ??? Example "Answers"
408 |
409 |
416 | ```APL
417 | +/(⍳17)*2
418 | 1785
419 | ```
420 | Without parentheses we get the sum of the first 289 integers, instead of the first 17 integers squared.
421 |
422 | |TMN|APL|
423 | |---|---|
424 | |$\sum_n^{17^2} n$|`+/⍳17*2`
425 | |$\sum_n^{17} n^2$|`+/(⍳17)*2`|
426 |
427 |
435 | We can either subtract 1 from the even numbers:
436 | ```APL
437 | +/(2×⍳100)-1
438 | 10000
439 | ```
440 |
441 | or we can add negative 1:
442 | ```APL
443 | +/¯1+2×⍳100
444 | 10000
445 | ```
446 | The high minus denotes a literal negative, whereas the hyphen indicates subtraction.
447 |
448 |
449 | Remember the right-to-left rule: functions take everything to their right, and the first thing to their left. We can add unnecessary parentheses to show how APL evaluates our expression.
450 | ```APL
451 | (84 - (12 - (1 - (13 - (28 - (9 - (6 - 15)))))))
452 | 70
453 | ```
454 |
455 |
456 |
457 | 1. Pyramid Schemes
458 | 1. Sugar cubes are stacked in an arrangement as shown by **Figure 1**.
459 |
460 |
461 | Figure 1. Stacked sugar cubes
462 |
463 | This stack has `4` **layers** and a total of `30` cubes. How many cubes are there in a similar stack with `467` **layers**?
464 |
465 | 1. Now consider the stack in **Figure 2**.
466 |
467 |
468 | Figure 2. Differently stacked sugar cubes
469 |
470 | The arrangement in **Figure 2** has `4` **layers** and `84` cubes. How many cubes are there in a similar stack with `812` **layers**?
471 |
472 | 1. Now look at **Figure 3**.
473 |
474 |
475 | Figure 3. This is just a waste of sugar cubes by now...
476 |
477 | The stack in **Figure 3** has `3` **"layers"** and `36` cubes in total. How many cubes are there in a similar stack with `68` **"layers"**?
478 |
479 | ???Example "Answers"
480 |
481 |
482 | Each $n$th layer has $n^2$ cubes. There are $34,058,310$ cubes in a stack with $467$ layers.
483 | ```APL
484 | +/(⍳4)*2
485 | ```
486 | ```
487 | 30
488 | ```
489 | ---
490 | ```APL
491 | +/(⍳467)*2
492 | ```
493 | ```
494 | 34058310
495 | ```
496 |
497 |
498 | Each $n$th layer has $(2n-1)^2$ cubes. There are $713,849,500$ cubes in a stack with $812$ layers.
499 | ```APL
500 | +/(¯1+2×⍳4)*2
501 | ```
502 | ```
503 | 84
504 | ```
505 | ---
506 | ```APL
507 | +/(¯1+2×⍳812)*2
508 | ```
509 | ```
510 | 713849500
511 | ```
512 |
513 |
514 | Each $n$th layer has $n^3$ cubes. There are $5,503,716$ cubes in a stack with $68$ layers.
515 | ```APL
516 | +/(⍳3)*3
517 | ```
518 | ```
519 | 36
520 | ```
521 | ---
522 | ```APL
523 | +/(⍳68)*3
524 | ```
525 | ```
526 | 5503716
527 | ```
528 |
529 |
530 |
531 | 1. Rewrite the following expressions so that they do not use parentheses.
532 | 1. `(÷a)×b`
533 | 1. `(÷a)÷b`
534 | 1. `(a+b)-5`
535 | 1. `(a+b)+5`
536 |
537 | ???Example "Answers"
538 |
539 |
Multiplication is commutative, which means that the order of arguments does not matter, so we can write `b×÷a`. Even more simply, it is `b÷a` because multiplication by a reciprocal is the same as division.
540 |
${{{1}\over{a}}\div{b}} = {{1}\over{a\times{b}}}$ so we can write `÷a×b`
541 |
Use a literal negative five:`¯5+a+b`
542 |
No parentheses needed: `a+b+5`
543 |
544 |
--------------------------------------------------------------------------------
/docs/selecting-from-arrays.md:
--------------------------------------------------------------------------------
1 | # Selecting from Arrays
2 |
3 | In an array-oriented language, perhaps it's no surprise that there are umpteen ways to select values from arrays. There are also many ways to [modify or assign values](./finding-and-replacing-values.md) within arrays.
4 |
5 | The exact terminology can vary between array languages, and even APLers use these words interchangeably sometimes. However, on this page we will say that:
6 |
7 | - **Scalars** (0-cells) are the things returned by indexing expressions
8 | - **Elements** (or **items**) are the arrays inside of scalars. For a simple scalar *this is the same thing*! [Remember enclosing and diclosing scalars before?](./multidimensional-and-nested-arrays.md#arrays-are-made-of-scalars).
9 |
10 | These notes summarise the different constructs available. There is also a [Dyalog webinar dedicated to selecting from arrays](https://dyalog.tv/Webinar/?v=AgYDvSF2FfU).
11 |
12 | ## Square bracket indexing
13 | This is the type of indexing we have been using so far. For vectors, it is very intuitive:
14 |
15 | ```APL
16 | 'LE CHAT'[6 4 1 2 3 5 6]
17 | THE CAT
18 | ```
19 |
20 | For higher rank arrays, we can return rectangular sub-arrays by separating the indices into each axis by semicolons:
21 |
22 | ```APL
23 | (2 3 4⍴⎕A)[1 2;1 3;1 4] ⍝ The corner elements of the cuboid
24 | AD
25 | IL
26 |
27 | MP
28 | UX
29 | ```
30 |
31 | 1. What happens if you omit an axis? For example, `array[3;4 5;;]`?
32 | 1. What happens if you use too many or too few semicolons?
33 |
34 | ## Squad (A.K.A. "Functional") indexing
35 | Square-bracket indexing requires you to know the exact rank of the array and have the correct number of semicolons in your indexing expression. You might also notice that it is a special or [anomalous syntax](https://aplwiki.com/wiki/APL_syntax#Syntactic_elements).
36 |
37 | There is also an **index** function `⍺⌷⍵` which has two distinctions:
38 |
39 | - It is a function with the same syntax as other functions
40 | - It applies to any rank array by automatically filling in less-major cells (those cells defined by trailing axes)
41 |
42 | ```APL
43 | (1 2)(2 3)⌷(2 3 4⍴⎕A)
44 | ```
45 | ```
46 | EFGH
47 | IJKL
48 |
49 | QRST
50 | UVWX
51 | ```
52 | ---
53 | ```APL
54 | (2 3 4⍴⎕A)[1 2;2 3;]
55 | ```
56 | ```
57 | EFGH
58 | IJKL
59 |
60 | QRST
61 | UVWX
62 | ```
63 |
64 | ## Take and drop
65 | We can chop off the edges of an array using **take** `⍺↑⍵` and **drop** `⍺↓⍵`.
66 | ```APL
67 | ¯1 3 2↑2 3 4⍴⎕A
68 | ```
69 | ```
70 | MN
71 | QR
72 | UV
73 | ```
74 | ---
75 | ```APL
76 | 1 0 ¯2↓2 3 4⍴⎕A
77 | ```
78 | ```
79 | MN
80 | QR
81 | UV
82 | ```
83 |
84 | !!! Note
85 | While similar subarrays can be retrieved using indexing, take or drop, note that *take* and *drop* **return** arrays of **the same rank** as their argument.
86 |
≢⍴1 1↑2 3 4⍴⎕A
87 | 3
88 | ≢⍴1 1⌷2 3 4⍴⎕A
89 | 1
90 |
91 | ## Simple indexing
92 | The selection of rectangular sub-arrays as demonstrated above using square brackets `[]` and squad `⌷` is also known as **simple indexing**.
93 |
94 | ## Choose indexing
95 | Simple indexing with square brackets uses scalars or vectors separated by semicolons. Index using square brackets and a nested array of numeric vectors and we can select any collection of scalars:
96 |
97 | ```APL
98 | (2 3 4⍴⎕A)[(1 1 1)(2 1 4)(1 3 4)]
99 | ```
100 | ```
101 | APL
102 | ```
103 |
104 | An interesting relationship appears between indices into an array and indices into its ravel when `⎕IO←0`:
105 |
106 | ```APL
107 | ⎕IO←0
108 | (2 3 4⍴⎕A)[↓[0]2 3 4⊤0 15 11]
109 | ```
110 | ```
111 | APL
112 | ```
113 | ---
114 | ```APL
115 | ⎕A⌷⍨⊂2 3 4⊥↑[0](0 0 0)(1 0 3)(0 2 3)
116 | ```
117 | ```
118 | APL
119 | ```
120 |
121 | ## Reach indexing
122 | Indexing into an array will retrieve some cell of an array. If it is a nested array, then selecting a scalar will return an enclosed array. Sometimes what you actually want is the item inside of that scalar.
123 |
124 | While it is common and perfectly valid to simply use *first* `⊃⍵` to disclose the contents of a scalar, the *pick* function `⍺⊃⍵` can be used to retrieve the element directly:
125 | ```APL
126 | 3⌷'here' 'are' 'some' 'words' ⍝ With ]Boxing on
127 | ┌─────┐
128 | │words│
129 | └─────┘
130 | 3⊃'here' 'are' 'some' 'words'
131 | words
132 | ```
133 |
134 | Reach indexing allows you to pull items from deep within a nested array:
135 | ```APL
136 | (2 1)(2 2) ⊃ 2 3⍴0 1 2 (2 3⍴'AB' 'CD' 'EF' 'GH' 'IJ' 'KL') 4 5
137 | IJ
138 | ```
139 |
140 | ## Select / From
141 | Some APLers find squad-index semantics awkward, and have proposed yet another mechanism, called **select** or [**from**](https://aplwiki.com/wiki/From). It can be defined as:
142 | ```APL
143 | I←(⊃⍤⊣⌷⊢)⍤0 99
144 | ```
145 |
146 | Select provides the best of both simple indexing and choose indexing, allowing you to select arbitrary collections of cells.
147 |
148 | !!! Warning
149 | Select is a very general and convenient function, but it is potentially much slower than using the in-built indexing constructs. We provide it here for completeness and your interest.
150 |
151 | ## So which type of indexing do I use?
152 | Over time you will learn from experience what is the most appropriate thing to use in different situations. However, here is a rough guide:
153 |
154 | |Selection type|Selection construct|
155 | |---|---|
156 | |Arbitrary scalars from a vector|Square bracket simple or [compress](./array-logic-data-driven-conditionals.md#replicatecompress)|
157 | |Rectangular subarrays|Simple|
158 | |Arbitrary scalars from an array of rank ≥2|Choose|
159 | |Nested arrays|Reach|
160 | |Arbitrary collections of cells|Select|
161 |
162 | ## Problem set 7
163 |
164 | ### Search, sort, slice and select
165 | 1. Anna, Ben and Charlie are having a competition. They want to see who can eat the most fruit in a week.
166 |
167 | ```APL
168 | fruits ← 4 7⍴'Apples MangoesOrangesBananas'
169 | days ← 7 3⍴'SunMonTueWedThuFriSat'
170 | names ← 3 7⍴'Anna Ben Charlie'
171 | ⎕RL ← 42 1 ⋄ ate ← ?3 4 7⍴3
172 | ```
173 |
174 | ???+Question "What is `⎕RL`?"
175 | The roll function `?⍵` generates random numbers for each simple scalar number in `⍵`.
176 |
177 | Setting the Random Link [system variable](./Quad%20names.md#system-variables) `⎕RL` lets us generate the same random numbers repeatedly.
178 |
179 | 1. Compute the names of the people who ate the most fruit on Tuesday and Sunday combined.
180 | 1. Compute the name of the person who ate the most mangoes and bananas combined.
181 | 1. What is the name of the person who ate the most fruit overall?
182 |
183 | ???Example "Answer"
184 | There are many different ways to find these answers. The following are just one set of solutions.
185 |
186 |
187 |
188 |
189 | Anna and Charlie both ate 10 fruits total on Tuesday and Sunday combined. Ben only ate 8 fruits.
190 |
191 | ```APL
192 | d←2 3⍴'TueSun'
193 | total ← +/+/ate[;;days⍳d]
194 | (total=⌈/total)⌿names
195 | ```
196 | ```
197 | Anna
198 | Charlie
199 | ```
200 |
201 |
202 |
203 |
204 | Charlie ate the most mangoes and bananas across the whole week.
205 |
206 | ```APL
207 | f←2 7⍴'MangoesBananas'
208 | total ← +/+/ate[;fruits⍳f;]
209 | (total=⌈/total)⌿names
210 | ```
211 | ```
212 | Charlie
213 | ```
214 |
215 |
216 |
217 |
218 | Anna ate the most fruit overall.
219 |
220 | ```APL
221 | total ← +/+/ate
222 | (total=⌈/total)⌿names
223 | ```
224 | ```
225 | Anna
226 | ```
227 |
228 | Any of these totals could have been expressed as a single sum. Either by ravelling submatrices for each person:
229 |
230 | ```APL
231 | total ← +/(,⍤2)ate
232 | ```
233 |
234 | Or by merging the last two axes:
235 |
236 | ```APL
237 | total ← +/,[2 3]ate
238 | ```
239 |
240 | A discussion comparing these expressions will be added later.
241 |
242 |
243 |
244 |
245 | 1. Write a function `FindWord` which accepts a character matrix left argument `⍺` and a character vector right argument `⍵` and returns a Boolean vector where a `1` indicates a row in `⍺` which matches the word `⍵`.
246 | ```APL
247 | fruits←↑'apples' 'mangoes' 'oranges' 'bananas'
248 | fruits FindWord 'apples'
249 | 1 0 0 0
250 | fruits FindWord 'oranges'
251 | 0 0 1 0
252 | ```
253 |
254 | !!!Question "What is `↑`?"
255 | We created a nested vector of different length character vectors using [strand notation](#arrays-are-made-of-arrays). The mix function `↑⍵` is used to turn this from a nested vector of vectors into a flat matrix made of simple character scalars. In order to make the matrix rectangular, shorter vectors are padded with spaces.
256 |
257 | ```APL
258 | ' '=↑'apples' 'mangoes' 'oranges' 'bananas'
259 | 0 0 0 0 0 0 1
260 | 0 0 0 0 0 0 0
261 | 0 0 0 0 0 0 0
262 | 0 0 0 0 0 0 0
263 | ```
264 |
265 | ???Example "Answer"
266 |
267 | There are many ways to solve this problem. A comparison of different approaches is worthy of a fuller discussion, which will be added later. For now we will simply show a few alternatives:
268 |
269 | ```APL
270 | FindWord ← {∧/∨/⍺∘.=⍵↑⍨2⌷⍴⍺}
271 | FindWord ← {∨/(⍵↑⍨⊢/⍴⍺)⍷⍺}
272 | FindWord ← {(⍵↑⍨⊢/⍴⍺)(≡⍤1)⍺}
273 | FindWord ← {⍺∧.=⍵↑⍨2⌷⍴⍺}
274 | ```
275 |
276 | 1. From the nested 3D array
277 |
278 | ```APL
279 | nest←2 3 4⍴(⍳17),(⊂2 3⍴'ab'(2 3⍴'dyalog'),'defg'),6↑⎕A
280 | ```
281 |
282 | use a single selection to obtain:
283 |
284 | 1. The character scalar `'y'`
285 | 1. The numeric scalar `6`
286 |
287 | ???Example "Answers"
288 | It can be tricky to simplify these to a single use of pick `⍺⊃⍵`. Although understanding these selections can help with understanding complicated nested array structures, it is not very common to need to do this in real code.
289 |
290 |
302 |
303 |
304 | 1. What type of indexing is used in the expression `grid[⍸grille=' ']` ?
305 |
306 | ???Example "Answer"
307 | Because `grille` is a matrix, the equality with the space character is also a matrix. The **where** function `⍸⍵` returns a nested vector of indices, which when used with square brackets forms a **choose indexing** expression.
308 |
309 | 1. What indexing array can be used to select a simple scalar from itself?
310 |
311 | ???Example "Answer"
312 | For choose indexing, an enclosed empty numeric vector:
313 |
314 | ```APL
315 | 'a'[⊂⍬]
316 | ```
317 | ```
318 | a
319 | ```
320 |
321 | For squad indexing, an empty numeric vector:
322 |
323 | ```APL
324 | ⍬⌷'a'
325 | ```
326 | ```
327 | a
328 | ```
329 |
330 | For reach indexing, either:
331 |
332 | ```APL
333 | ⍬⊃'a'
334 | ```
335 | ```
336 | a
337 | ```
338 | ---
339 | ```APL
340 | (⊂⍬)⊃'a'
341 | ```
342 | ```
343 | a
344 | ```
345 |
346 | 1. Define `n←5 5⍴⍳25` in your workspace.
347 |
348 | Using selections, find at least four different ways to set the bottom-right 3 by 3 submatrix in `n` to `0`.
349 | For example, `(2 2↓n)←0`.
350 |
351 | ??? Hint
352 | See which primitives may be used in a selective assignment
353 |
354 | ???Example "Answers"
355 | Compute the indices:
356 |
357 | ```APL
358 | n[2+⍳3;2+⍳3]←0
359 | ```
360 |
361 | Use negative take:
362 |
363 | ```APL
364 | (¯3 ¯3↑n)←0
365 | ```
366 |
367 | Use two compressions:
368 |
369 | ```APL
370 | b←2 3/0 1
371 | (b/b⌿n)←0
372 | ```
373 |
374 | Positive take after reversals:
375 |
376 | ```APL
377 | (3 3↑⌽⊖n)←0
378 | ```
379 |
380 | ### Visit to the museum
381 | Here are some data and questions about visits to a museum.
382 |
383 | The `section_names` are the names of each of the four sections in the museum.
384 |
385 |
section_names ← 'Bugs' 'Art' 'Fossils' 'Sea Life'
386 |
387 | The variable `sections` is a nested list of text matrices. Each matrix lists the items or creatures which belong to each section.
388 |
389 |
sections ← ↑¨('Grasshopper' 'Giant Cicada' 'Earth-boring Dung Beetle' 'Scarab Beetle' 'Miyama Stag' 'Giant Stag' 'Brown Cicada' 'Giraffe Stag' 'Horned Dynastid' 'Walking Stick' 'Walking Leaf') ('The Blue Boy by Thomas Gainsborough' ('Rooster and Hen with Hydrangeas by It',(⎕ucs 333),' Jakuch',(⎕ucs 363)) 'The Great Wave off Kanagawa by Hokusai' 'Mona Lisa by Leonardo da Vinci' 'Sunflowers by Vincent van Gogh' 'Still Life with Apples and Oranges by Paul Cézanne' 'Girl with a Pearl Earring by Johannes Vermeer' ('Shak',(⎕ucs 333),'ki dog',(⎕ucs 363),' by Unknown') 'David by Michelangelo di Lodovico Buonarroti Simoni' 'Rosetta Stone by Unknown') ('Amber' 'Ammonite' 'Diplodocus' 'Stegosaur' 'Tyrannosaurus Rex' 'Triceratops') ('Puffer Fish' 'Blue Marlin' 'Ocean Sunfish' 'Acorn Barnacle' 'Mantis Shrimp' 'Octopus' 'Pearl Oyster' 'Scallop' 'Sea Anemone' 'Sea Slug' 'Sea Star' 'Whelk' 'Horseshoe Crab')
390 |
391 | The `visits` table represents 1000 visits to museum sections over a four week period. The four columns represent:
392 |
393 | - The section that was visited as an index into the `section_names`
394 | - The day of the visit in [Dyalog Date Number](http://help.dyalog.com/latest/#Language/System%20Functions/dt.htm) format.
395 | - The arrival time in minutes from midnight. For example, 15:30 is 930 minutes.
396 | - The departure time in minutes from midnight.
397 |
398 |
400 |
401 | In the boolean matrix `display`, each row corresponds to a museum piece and each column corresponds to a day. A `1` indicates days when a particular museum piece was out on display. The order of rows corresponds to the order of pieces in the `sections` table.
402 |
403 |
405 |
406 | 1. How many visitors arrived before 10AM?
407 | 1. What was the most popular section by visit duration?
408 | 1. Estimate the opening and closing times of each of the sections.
409 | 1. Which animal being on display corresponded with the highest increase in visit duration for its section?
410 |
--------------------------------------------------------------------------------
/docs/user-defined-functions.md:
--------------------------------------------------------------------------------
1 | # User defined functions
2 | This is a fairly brief introduction, and there are exercises at the end of this section to help solidify your understanding, but there is also [a more extensive treatment of user-defined functions in the book Mastering Dyalog APL](https://mastering.dyalog.com/User-Defined-Functions.html).
3 |
4 | ## The Three Function Styles
5 |
6 | So far, we have been reading and writing [dfns](https://aplwiki.com/wiki/Dfn).
7 |
8 | ```APL
9 | 3 {⍺+⍵} 5 ⍝ Left argument ⍺, right argument ⍵
10 | {⍵>0:⍵,∇ ⍵-1 ⋄ ⍵}5 ⍝ Guard is : (colon). The function itself is ∇ (del)
11 | Fn ← {⍺⍵} ⍝ We can give functions names
12 | ```
13 |
14 | It is also possible to name functions which do not explicitly refer to their arguments. This is called [tacit](https://aplwiki.com/wiki/Tacit_programming) or *point-free* programming.
15 |
16 | ```APL
17 | Plus ← +
18 | IndicesTo ← ⍳
19 | _Reduce ← /
20 | Sum ← Plus _Reduce
21 | Sum IndicesTo 10
22 | ```
23 |
24 | There is a syntax for composing functions called [*trains*](https://aplwiki.com/wiki/Tacit_programming#Trains).
25 |
26 | A two-train is an **atop**:
27 |
28 | ```APL
29 | 3(|-)5
30 | 2
31 | |3-5
32 | 2
33 | ```
34 |
35 | A three-train is a **fork**:
36 |
37 | ```APL
38 | 3(-×+)5
39 | ¯16
40 | (3-5)×(3+5)
41 | ¯16
42 | ```
43 |
44 | Any further functions simply alternate between *atop* (even number of functions) and *fork* (odd number of functions).
45 |
46 | ```APL
47 | 3(|-×+)5 ⍝ Absolute value of the product of sum and difference
48 | 16
49 | 3(⌈|-×+)5 ⍝ Max residue with the product of sum and difference
50 | 4
51 | ```
52 |
53 | They allow some rather neat and memorable ways to write short functions.
54 |
55 | ```APL
56 | Mean ← +⌿ ÷ ≢ ⍝ The sum divided by the count
57 | Mean 3 1 4 1
58 | 3 (+,-) 5 ⍝ Plus and minus
59 | ','(≠⊆⊢)'some,text' ⍝ Split on commas
60 | ```
61 |
62 | !!! Note
63 | Small unnamed dfns and tacit functions expand your vocabulary. One of my favourites is the "split by delimiter" train (≠⊆⊢). It looks like a beat-up face kaomoji. A similar phrase which can take multiple delimiters can be found on aplcart.info.
64 |
65 | ## Traditional functions
66 | Dyalog is a modern APL implementation. Since early APL implementations there has been a way of defining functions with a header line and named arguments and results. Since the introduction of dfns, functions of the original style are called *traditional functions* or [*tradfns*](https://aplwiki.com/wiki/Defined_function).
67 |
68 | ```APL
69 | Mean ← +⌿÷≢ ⍝ A 3-train (fork) for the arithmetic mean
70 | ```
71 | ```
72 |
73 | ```
74 | ```
75 | ```
76 | ---
77 | ```APL
78 | Mean ← {(+⌿⍵)÷≢⍵} ⍝ A dfn for the arithmetic mean
79 | ```
80 | ```
81 |
82 | ```
83 | ```
84 | ```
85 | ---
86 | ```APL
87 | ∇ m ← Mean a ⍝ A tradfn for the arithmetic mean
88 | m ← (+⌿a) ÷ ≢a
89 | ∇
90 | ```
91 | ```
92 |
93 | ```
94 | ```
95 | ```
96 |
97 | !!! Note
98 | Copy and paste everything between (and including) the two ∇del symbols into the session, and press Enter, to define a tradfn in your workspace.
Using Shift+Enter with the cursor on a name will bring up an editor window for that named thing.
99 |
100 | A tradfn header reflects the calling syntax of the function.
101 |
102 | ```APL
103 | ∇ {result}←{optional}Tradfn argument;local1
104 | [1] ;local2 ⍝ Locals can be declared across multiple lines in the header
105 | [2] :If 0=⎕NC'optional'
106 | [3] optional←'no left argument'
107 | [4] :EndIf
108 | [5] local1←'⍺: ',optional
109 | [6] local2←'⍵: ',argument
110 | [7] global←⍪'TradFn last called with'local1 local2
111 | [8] result←⍪local1 local2
112 | ∇
113 | ```
114 |
115 | !!! Note
116 | The ∇ del representation of the TradFn function above is the vector representation result of ⎕VR'TradFn' which can be directly input into the session.
117 |
118 | 1. Try calling `TradFn` with one and two arguments. How can the result be made to display to the session?
119 | 1. Inspect the variable `global` after calling `TradFn` with different arguments.
120 | 1. Step through the function using `Ctrl+Enter`. Inspect `⎕NC'optional'` when `TradFn` is called with one argument and when it is called with two arguments.
121 |
122 | Here is the smallest tradfn:
123 | ```APL
124 | ∇ T
125 | ∇
126 | ```
127 |
128 | `T` is a function which takes no arguments, returns no results and has no effects.
129 |
130 | Results in `{}` curly braces are called **shy results** and do not print to the session by default, but can be passed to arguments. To ease debugging and write functions with predictable behaviour, it is generally best not to use shy results.
131 |
132 | Optional left arguments are a little awkward in tradfns. The dfn equivalent is a little nicer looking: `{⍺←'default' ⋄ ⍺,⍵}`.
133 |
134 | ## Name scope, locals and globals
135 | The scope of a name describes the circumstances under which it is visible to code.
136 |
137 | For most intents and purposes, you just need to know about the difference between how **local** and **global** names are defined in the syntax, and how **name shadowing** works.
138 |
139 | By default, names assigned in tradfns are global. This is mostly for historical reasons. Names declared in the header - the arguments, results, and names preceded by semicolons - are localised.
140 |
141 | !!!Note "Traditional function header example"
142 | ```APL
143 | result ← left FunctionName right;var1;var2
144 | ```
145 | The header for a function called `FunctionName`. The names `result`, `left`, `right`, `var1` and `var2` are local.
146 |
147 | By default, names in a dfn are local to that dfn. This is the preferred default in most modern programming languages.
148 |
149 | If we define a name in the current namespace, that name is visible only within that namespace unless referred to by its full namespace path (e.g. `#.nsref.var`).
150 |
151 | ```APL
152 | 'ns1'⎕ns⍬ ⋄ ns1.var←1 2 3
153 | 'ns2'⎕ns⍬ ⋄ ⎕cs ns2
154 | ⎕←var
155 | VALUE ERROR: Undefined name: var
156 | ⎕←var
157 | ∧
158 | ⎕←#.ns1.var
159 | 1 2 3
160 | ```
161 |
162 | Let us now define a dfn and a tradfn in `#.ns1`:
163 |
164 | ```APL
165 | ⎕cs #.ns1
166 |
167 | ∇ Dfn←{
168 | [1] var←'lexical'⍵
169 | [2] }
170 | ∇
171 |
172 | ∇ Tradfn arg
173 | [1] var←'dynamic'arg
174 | ∇
175 | ```
176 |
177 | !!! Note
178 | While the `∇` *del* representation of dfns can be used to define dfns in the session, dfns in scripted namespaces must be defined without `∇` dels.
179 |
180 | If we call each of these functions, `Tradfn` will modify `var` in the current namespace, but `Dfn` will not:
181 |
182 | ```APL
183 | Dfn var
184 | var
185 | ```
186 | ```
187 | 1 2 3
188 | ```
189 | ---
190 | ```APL
191 | Tradfn var
192 | var
193 | ```
194 | ```
195 | ┌───────┬─────┐
196 | │dynamic│1 2 3│
197 | └───────┴─────┘
198 | ```
199 |
200 | In the following definition, `var⊢←` will update `var` in the closest scope where it is localised - in this case `#.ns1`.
201 |
202 | ```APL
203 | ∇ Dfn←{
204 | [1] var⊢←'lexical'⍵
205 | [2] }
206 | ∇
207 | ```
208 |
209 | In Tradfns, references to local names within a function are said to "*shadow*" the same names from outer scopes. Notice how the following definition of `Tradfn` fails.
210 |
211 | ```APL
212 | ∇ Tradfn arg;var
213 | [1] var,←'dynamic'arg
214 | ∇
215 | ```
216 |
217 | A similar dfn succeeds because, in dfns, [modified assignment](./Assignment.md#modified-assignment) will search the local scope and then any parent scopes.
218 |
219 | ```APL
220 | ∇ Dfn←{
221 | [1] var,←'lexical'⍵
222 | [2] }
223 | ∇
224 | ```
225 |
226 | For completeness, here we will also mention `⎕SHADOW`. It is used when names are dynamically created using `⍎`, `⎕FX` or `⎕FIX` but need to be localised. However, it is best to use the function syntax to establish name scope in general. Further information can be found [in the specialists section on shadowed names](https://www.dyalog.com/uploads/documents/MasteringDyalogAPL.pdf#page=252) in Mastering Dyalog APL.
227 |
228 | The technical distinction between dfns and tradfns is that tradfns have **dynamic scope** whereas dfns have **lexical scope**.
229 |
230 | For further explanation of how this affects the use of dfns, see [section 5.5.3 of Mastering Dyalog APL](https://mastering.dyalog.com/User-Defined-Functions.html#lexical-scoping).
231 |
232 | ### Avoid globals
233 | When possible, avoid using global variables.
234 | Pass parameters to functions as arguments unless this becomes very awkward.
235 | The use of global variables should be limited to state settings that affect the entire application, or tables containing databases that are shared globally.
236 | If you need global constants, it is a good idea to create them in a function in order to be able to use source code management / change tracking software.
237 |
238 | A function which uses globals is difficult, if not impossible, to run in parallel. If two copies of the function run in parallel and they update the global data, some kind of locking is required. Locking often defeats the potential benefits of parallel execution.
239 |
240 | Names should be localized unless they really really, really, really need to be global.
241 |
242 | An example of when to use globals is a switch which affects the entire application:
243 |
244 | ```APL
245 | ∇ err←Log msg
246 | [1] :If verbose
247 | [2] ⎕←msg ⍝ Display information to the user
248 | [3] :EndIf
249 | [4] PrintFile msg
250 | ∇
251 | ```
252 |
253 | ## Nested functions
254 | It is possible to define functions inside some other functions.
255 |
256 | - Tacit functions can only include other user-defined functions by name
257 |
272 |
273 | ## Which style to use?
274 |
275 | While usage of different function styles varies throughout many applications, you might take inspiration from [Adám's APL Style Guide](https://abrudz.github.io/style/), when writing brand new production code. When maintaining others' code, it is best to try to continue in the already established style.
276 |
277 | #### Dfns
278 | For medium sized functions and utilities. Nested dfns are fine, but never use multi-line dfns inline.
279 |
280 | ```APL
281 | MultiDfn←{ ⍝ A Dfn with nested in-line multi-line dfns
282 | (3{ ⍝ These are confusing to read and trace through
283 | ⍺+2×⍵
284 | }⍺){
285 | x←⍺-4+⍵
286 | x-2×⍺
287 | }3+⍵
288 | }
289 | ```
290 |
291 | Instead, give them names and then apply them. Named dfns should be multi-line so that they can be traced through, unless truly trivial.
292 |
293 | ```APL
294 | MultiDfn2←{ ⍝ The previous function rewritten more clearly
295 | y←3+2×⍺
296 | x←y-1+⍵
297 | x-2×y
298 | }
299 | ```
300 |
301 | Do not use a dfn instead of naming a variable. For example, instead of
302 |
303 | ```APL
304 | r←{⍵/⍨10≤⍵}a,b
305 | ```
306 |
307 | write
308 |
309 | ```APL
310 | candidates←a,b
311 | r←candidates/⍨10≤candidates
312 | ```
313 |
314 | #### Tacit functions
315 | Best as short, [pure functions](https://en.wikipedia.org/wiki/Pure_function), performing some specific task such as data transformation. Trains and functions derived from functions and operators (e.g. `+/`) can be used inline if they are not too complex.
316 |
317 | #### Tradfns
318 | Best used as program control and for dealing with system interactions. The use of control structures can make procedural tasks easier to debug. For example, if an error occurs during a loop or iteration.
319 |
320 | ```APL
321 | ¯5{i←⍺+⍳⍵ ⋄ i÷i-2}10 ⍝ A single line function cannot be traced through
322 | ```
323 |
324 | !!! Note
325 | Use Ctrl+Enter to step through a multiline function. You can then use Shift+Enter to edit the function during execution and Esc to save your changes to the function and continue execution.
326 |
327 | ```APL
328 | ∇ r←a MultiLineError o;i
329 | [1] :For i :In a+⍳o
330 | [2] r←i+3
331 | [3] r÷r-2
332 | [4] :EndFor
333 | ∇
334 | ```
335 |
336 | ## Problem set 10
337 |
338 | ### Which style again?
339 | 1. Which of the following function styles can have multiple lines?
340 | 1. TradFns
341 | 1. Dfns
342 | 1. Tacit functions
343 | 1. Which of the following function styles can be anonymous (unnamed)?
344 | 1. Tradfns
345 | 1. Dfns
346 | 1. Tacit
347 | 1. Think about which function style would be most appropriate in the following situations.
348 | 1. Launching an application
349 | 1. Applying a mathematical formula to an array of values
350 | 1. A utility function to sort an array
351 | 1. Reading and writing files
352 | 1. Expressing the sum of two functions (f+g)(x)
353 | 1. Downloading data from the internet
354 | 1. GUI programming
355 | 1. Checking if a function is a [no-op](https://en.wikipedia.org/wiki/NOP_(code)) for a particular array
356 | 1. Defining a [piecewise](https://www.mathsisfun.com/sets/functions-piecewise.html) mathematical function
357 |
358 | ### Choo choo
359 | 1. Translating functions
360 | 1. Convert the following dfns into trains
361 | 1. `{⌈/≢¨⍵}`
362 | 1. `{1+⍺-⍵}`
363 | 1. `{∨/⍺∊⍵}`
364 | 1. `{(⌽⍵)≡⍵}`
365 | 1. Convert the following trains into dfns
366 | 1. `(⌈/-⌊/)`
367 | 1. `(+⌿÷1⌈≢)`
368 | 1. `(⊢-|)`
369 | 1. `(1∧⊢,÷)`
370 |
371 | ### Marking Tests
372 | Way back in [problem set 3](./array-logic-data-driven-conditionals.md#problem-set-3) you wrote a dfn to convert test scores into letter values.
373 |
374 | You were led to produce some function or expression similar to the following:
375 |
376 | ```APL
377 | Grade←{'FDCBA'[+/⍵∘.>80 70 60 50 0]}
378 | Grade 95 65 92 77
379 | ```
380 |
381 | This is an array-oriented solution to this problem. However, if a human was manually grading test scores, they might take one scored paper at a time and decide on which letter grade to write by reading each score.
382 |
383 | Procedural [pseudocode](https://en.wikipedia.org/wiki/Pseudocode):
384 |
385 | ```pseudocode
386 | scores = 93,85,45,10,70,16,93,63,41,7,95,45,76
387 | For each score in scores:
388 | If score is greater than 80:
389 | Write "A"
390 | Else If score is greater than 70:
391 | Write "B"
392 | Else If score is greater than 60:
393 | Write "C"
394 | Else If score is greater than 50:
395 | Write "D"
396 | Else
397 | Write "F"
398 | ```
399 |
400 | Control Structures in Dyalog are keywords beginning with a `:` colon.
401 |
402 | ```APL
403 | :If :OrIf :AndIf :ElseIf :Else :EndIf
404 | :For :In :EndFor
405 | :While :EndWhile
406 | :Repeat :Until :Return
407 | ```
408 |
409 | 1. Translate the pseudocode above into a **tradfn** called `Grade2` using control structures.
410 |
411 | 1. Rewrite the `Grade` function again as either a dfn or a tradfn called `Grade3` which uses `⍺⍸⍵` interval index.
412 |
413 | 1. Use the `]runtime` user command to compare the computation time for each of the three grading functions.
414 |