├── 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 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 54 | 56 | 58 | 64 | 65 | 66 | 70 | 73 | 76 | 83 | 84 | -------------------------------------------------------------------------------- /docs/img/line.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 55 | 57 | 59 | 65 | 66 | 67 | 71 | 74 | 77 | 84 | 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 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 55 | 57 | 59 | 65 | 66 | 67 | 71 | 74 | 81 | 84 | 85 | -------------------------------------------------------------------------------- /docs/img/dyalog-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 |
  1. 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 |
  2. 119 |
  3. 120 | The shape of any scalar is an empty numeric vector. 121 | ```APL 122 | ⍴0 123 | ``` 124 | ``` 125 |   126 | ``` 127 | --- 128 | ```APL 129 | ⍴35 130 | ``` 131 | ``` 132 |   133 | ``` 134 | --- 135 | ```APL 136 | ⍴'Q' 137 | ``` 138 | ``` 139 |   140 | ``` 141 | --- 142 | ```APL 143 | ⍬≡⍴42 144 | ``` 145 | ``` 146 | 1 147 | ``` 148 |
  4. 149 |
  5. 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 |
  6. 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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |
⎕A⎕DM⎕DMX⎕PATH⎕SM
⎕TRAP⎕AN⎕EN⎕PP⎕STACK
⎕TS⎕AV⎕FR⎕PW⎕TC
⎕USING⎕AVU⎕IO⎕RL⎕THIS
⎕WA⎕CT⎕LC⎕RSI⎕TID
⎕WSID⎕D⎕LX⎕RTL⎕TNAME
⎕WX⎕DCT⎕ML⎕SE⎕TNUMS
⎕XSI⎕DIV⎕NULL⎕SI⎕TPOOL
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 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 |
⎕A⎕DM⎕DMX⎕PATH⎕SM
⎕TRAP⎕AN⎕EN⎕PP⎕STACK
⎕TS⎕AV⎕FR⎕PW⎕TC
⎕USING⎕AVU⎕IO⎕RL⎕THIS
⎕WA⎕CT⎕LC⎕RSI⎕TID
⎕WSID⎕D⎕LX⎕RTL⎕TNAME
⎕WX⎕DCT⎕ML⎕SE⎕TNUMS
⎕XSI⎕DIV⎕NULL⎕SI⎕TPOOL
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 |
  1. 445 | 446 | ```APL 447 | ('∆'@1)2 3⍴'DYALOG' 448 | ``` 449 | ``` 450 | ∆∆∆ 451 | LOG 452 | ``` 453 | 454 |
  2. 455 |
  3. 456 | 457 | ```APL 458 | ((2 3⍴3/'∆⍥')@1 2)2 3⍴'DYALOG' 459 | ``` 460 | ``` 461 | ∆∆∆ 462 | ⍥⍥⍥ 463 | ``` 464 | 465 |
  4. 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 |
      dfns←⎕NS⍬
242 |       'dfns.dws' Into dfns
243 |       dfns.disp dfns.morse 'SOS'
244 | ┌───┬───┬───┐
245 | │...│---│...│
246 | └───┴───┴───┘
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 |
      ns←⎕NS⍬
253 |       ns.(aa bb)←10 20
254 |       ns your_function 'aa' 'bb'
255 |       ns.(aa bb)
256 | 20 10
257 | 258 | 1. Write an expression that swaps the values of the variables named `x` in the namespaces `ns1` and `ns2` . 259 |
      ns1←⎕NS⍬ ⋄ ns2←⎕NS⍬
260 |       ns1.x←10 ⋄ ns2.x←20
261 |       ns1.y←30 ⋄ ns2.y←40
262 |       your_expression
263 |       ns1.x ns1.y ns2.x ns2.y
264 | 20 30 10 40
265 | 266 | 267 | 1. Write a function ScalarRef that returns a scalar Boolean value indicating whether its argument is a scalar namespace. 268 |
      ns←⎕NS⍬ ⋄ ns.a←10
269 |       ]disp ScalarRef¨ ns.a 'abc' (ns ns) ns (⎕ns⍬) 42
270 | 0 0 0 1 1 0
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 |
      ]disp RefMask (⊂⊂⊂⊂1 2 ns) 3 ns (2 2⍴'abc',⎕NS⍬)
282 | ┌─────────────┬─┬─┬───┐
283 | │┌───────────┐│0│1│0 0│
284 | ││┌─────────┐││ │ │0 1│
285 | │││┌───────┐│││ │ │   │
286 | ││││┌─────┐││││ │ │   │
287 | │││││0 0 1│││││ │ │   │
288 | ││││└─────┘││││ │ │   │
289 | │││└───────┘│││ │ │   │
290 | ││└─────────┘││ │ │   │
291 | │└───────────┘│ │ │   │
292 | └─────────────┴─┴─┴───┘
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 |
      FindRoot ⎕SE.Dyalog.Utils
308 | ⎕SE
309 |       FindRoot #
310 | \#
311 |       FindRoot ⎕NS⍬
312 | \#
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 |
:While LikelyToBeFales
209 | 	:AndIf ExpensiveTest
210 | 211 | is probably better than 212 | 213 |
:While LikelyToBeFales∧ExpensiveTest
214 | 215 | You will also often see: 216 | 217 |
:While PreCondition
218 | 	:AndIf OnlyComputableIfPreCondition
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 |
56 |
57 | [`+`](# "Plus"){ .glyph} 58 | [`-`](# "Minus/Negate"){ .glyph} 59 | [`×`](# "Times/Sign"){ .glyph} 60 | [`÷`](# "Divide/Inverse"){ .glyph} 61 | [`|`](# "Residue/Magnitude"){ .glyph} 62 | [`*`](# "Power"){ .glyph} 63 | [`⍟`](# "Logarithm"){ .glyph} 64 | [`⌈`](# "Max/Ceiling"){ .glyph} 65 | [`⌊`](# "Min/Floor"){ .glyph} 66 |
67 |
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 |
  1. 329 | ```APL 330 | ⌈/t_allweek 331 | 14.2 332 | ``` 333 |
  2. 334 |
  3. 335 | ```APL 336 | ⌊/t_allweek 337 | 6.7 338 | ``` 339 |
  4. 340 |
  5. 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 |
  6. 370 |
  7. 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 |
  8. 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 |
  1. 410 | ```APL 411 | ×/⍳12 412 | 479001600 413 | ``` 414 |
  2. 415 |
  3. 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 |
  4. 428 |
  5. 429 | ```APL 430 | +/2×⍳100 431 | 10100 432 | ``` 433 |
  6. 434 |
  7. 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 |
  8. 448 |
  9. 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 |
  10. 455 |
456 | 457 | 1. Pyramid Schemes 458 | 1. Sugar cubes are stacked in an arrangement as shown by **Figure 1**. 459 | 460 | Stacked sugar cubes 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 | Differently stacked sugar cubes 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 | This is just a waste of sugar cubes by now... 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 |
  1. 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 |
  2. 497 |
  3. 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 |
  4. 513 |
  5. 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 |
  6. 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 |
  1. 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.
  2. 540 |
  3. ${{{1}\over{a}}\div{b}} = {{1}\over{a\times{b}}}$ so we can write `÷a×b`
  4. 541 |
  5. Use a literal negative five:`¯5+a+b`
  6. 542 |
  7. No parentheses needed: `a+b+5`
  8. 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 |
  1. 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 |
  2. 202 |
  3. 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 |
  4. 216 |
  5. 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 |
  6. 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 |
  1. 291 | ```APL 292 | (2 2 2)(1 2)(1 2)⊃nest 293 | y 294 | ``` 295 |
  2. 296 |
  3. 297 | ```APL 298 | (⊂1 2 2)⊃nest 299 | 6 300 | ``` 301 |
  4. 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 |
⎕RL←42 ⋄ days←43589+?1000⍴28 ⋄ (arr lïv)←539+?2⍴⊂1000⍴510 ⋄ section←?1000⍴4
399 | visits←(⊂⍋days)⌷section,days,(⊂∘⍋⌷⊢)⍤1⍉↑arr lïv
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 |
display ← 40 28⍴(9/0),1,(4/0),1,(9/0),1,(3/0),(5/1),0,(10/1),0,(5/1),0,(8/1),0,(8/1),0,(4/1),0 0 1 1 0 1 0 1 0 1 1 0 1 0,(5/1),(3/0),1 0 1 0 1 1,(4/0),1 0 1 1 0 0 1 1 0,(6/1),0 1 0 1 0 0 1 1 0 0 1 1 0 1 0 0 1 1 0,(3/1),(3/0),(4/1),0 1 1 0 1 0 0,(7/1),0 1 0 1 1 0 1 1 0 1 1 0,(3/1),0 1 1 0,(4/1),0,(3/1),0 1 0,(3/1),0 0 1 1,(5/0),1 1 0,(3/1),0 1 0 0 1 1,(3/0),(5/1),0,(9/1),0,(3/1),0 1,(3/0),(5/1),0,(3/1),0,(3/1),(3/0),1 1 0 0 1 0 1,(4/0),1 1 0 1 0 1 0 1 0,(9/1),0,(7/1),0,(3/1),0 0 1 1 0 1 1 0 0 1 0 0 1 0,(5/1),0 1,(3/0),1 1 0 1 0 0,(3/1),0,(4/1),0 0 1 1,(7/0),(3/1),(3/0),1 1,(3/0),1 1 0 1 0 1,(6/0),1 1,(4/0),1 0 1 1,(5/0),1 0 1 0 1,(6/0),(3/1),(9/0),1 1,(3/0),1 0 1 0 1 1,(13/0),1 1,(11/0),1 0 1 1,(4/0),1 0 0,(4/1),0,(12/1),0,(5/1),0 1 0 0 1 1 0,(5/1),0,(4/1),0,(4/1),0 0 1,(5/0),1 1,(3/0),(8/1),0 0 1,(3/0),1,(3/0),1,(3/0),1 0 0 1 0 1 0 1 0 1 0 1 1 0,(3/1),(4/0),(3/1),0,(3/1),0 1 1,(3/0),(4/1),0 1 1 0 1 1,(3/0),1 1 0 1 0 1 0 1,(6/0),1 1,(14/0),(8/1),(4/0),(8/1),0,(3/1),0,(4/1),(6/0),1 0 0 1 1,(3/0),1 1 0 0 1 0 1 0 0 1 0 1,(5/0),1 0 0 1 0 1 0 0 1 1,(3/0),1,(8/0),1 0 1 0,(6/1),0 0,(7/1),0 1 1 0,(3/1),0,(9/1),0,(12/1),0 1 1 0,(9/1),0,(3/1),0 0,(3/1),(3/0),(3/1),0,(3/1),(5/0),(7/1),0 1 0,(5/1),0,(3/1),0 0,(3/1),0 0 1 1 0,(4/1),0 1,(3/0),(3/1),(5/0),1 0 1 1 0 1 0,(3/1),0,(5/1),0,(3/1),0,(4/1),0 1,(4/0),1 0 1 0 0 1 1,(5/0),1,(3/0),1 0 0 1 0 1,(3/0),1 0 1 0 0 1,(4/0),1 0 0 1,(6/0),1,(14/0),1 0 0,(4/1),(3/0),(6/1),0 0 1 0,(3/1),0,(4/1),0,(3/1),0 1 0 1,(3/0),(5/1),(3/0),1 0 0 1 0,(3/1),0 1,(4/0),1 0 1 1,(11/0),1,(15/0),(3/1),(4/0),1,(15/0),(5/1),0 1 0,(8/1),0,(3/1),(4/0),(5/1),0 1,(9/0),1 0 1 1 0 0 1 0 0 1,(4/0),1 0,(4/1),0,(7/1),(3/0),1 0 0 1,(3/0),(3/1),0 1 1
404 | 
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 |
      Sort ← {(⊂⍋⍵)⌷⍵}
258 | 	  CSI ← Sort⍥⎕C   ⍝ Case-insensitive sort
259 | 260 | - Dfns can contain tacit definitions and dfn definitions, as well as any named user-defined functions 261 |
 SortedMeans ← {
262 |     Sort ← {(⊂⍋⍵)⌷⍵}
263 |     Mean ← +⌿÷1⌈≢
264 |     Sort Mean¨⍵
265 | }
266 | - Tradfns can contain tacit definitions, dfn definitions and any named user-defined functions 267 |
     ∇ result←SortedMeans vectors;Mean;Sort
268 | [1]    Sort←{(⊂⍋⍵)⌷⍵}
269 | [2]    Mean←+⌿÷1⌈≢
270 | [3]    result←Sort Mean¨vectors
271 |      ∇  
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 |
]runtime -c "Grade 10×⍳10" "Grade2 10×⍳10" "Grade3 10×⍳10"
415 | --------------------------------------------------------------------------------