Conversation started Jan 31, 2018 at 18:30.
Jan 31, 2018 18:30
Welcome to the APL learning session!
Before we begin, let me just announce that next week's lesson will be on Tuesday rather than Wednesday, due to internal scheduling in Dyalog.
I've heard the request a few times for an intro to Object Orientation in APL. Any other subject requests for this lesson?
@Adám control structures
@EriktheOutgolfer Let's do that then. Should be fairly straight-forward.
Dyalog APL (and most modern APL implementations) support a fairly broad set of control structures. These use keywords (not system functions) and to avoid reserved words, they all begin with a colon, e.g. :If
As you may know, colons are also used in dfns' guards, but no colon-keywords are allowed in dfns, so there's no ambiguity.
Colons may also indicate labels in tradfns, but since keywords generally must be the first word in a statement, and labels must be the first thing on a line, there is again no conflict. Labels have trailing colon, keywords have leading colon. Thus label::If is valid and unambiguous.
Without further ado: :If
If you've used other structured programming languages, you'll find :If very familiar.
[1]   :If AGE<21
[2]       expr 1
[3]       expr 2
[5]   :EndIf
@Adám uh, why was line 4 skipped there
There's no "then" in APL, rather the next statement begins the "then" block. This means that you can inline If-statements:
@EriktheOutgolfer No reason. Just shows the beginning and end of the block. I guess a vertical ellipsis would have been nice.
:If AGE<21 ⋄ expr 1 ⋄ expr 2 ⋄ :EndIf
This rule applies to all control structures.
You can also have else:
[1]   :If AGE<21
[2]       expr 1
[3]       expr 2
[5]   :Else
[6]       expr 3
[7]   :EndIf
And you can add more conditions later with :ElseIf:
[1]   :If WINEAGE<5
[2]       'Too young to drink'
[5]   :ElseIf WINEAGE<10
[6]       'Just Right'
[7]   :ElseIf WINEAGE<15
[8]       'A bit past its prime'
[9]   :Else
[10]     'Definitely over the hill'
[11]  :EndIf
You can also go more exotic by adding multiple conditions with :AndIf or :OrIf. :AndIfs will only execute if all previous conditions were true, and :OrIfs will only execute if all previous conditions were false.
Maybe here I should mention that for the purposes of control structures with conditions, only simple (non-nested) singleton Boolean (0 or 1) arrays are valid.
@Adám apart from that blatant skipping of lines 3 and 4, is the best age range to drink really 5-9? :-D
Jan 31, 2018 18:45
For obvious ambiguity reasons, you can't mix :AndIf and :OrIf in a single condition block.
However, you can have non-conditional statements between the :Ifs and they will be executed if the conditions above them indicate so.
> :OrIfs will only execute if all previous conditions were false.
what
@betseg Meaning if any of the conditions (in order) are true, APL will immediately jump to the execution of the block.
ohh got it
[1]   :If WINE.NAME≡'Chateau Lafitte'
[2]       'Nice'
[3]   :OrIf WINE.YEAR∊1962 1967 1970⊣⎕←'Really?'
[4]       'The greatest?'
Here if the wine name is Chateau Lafitte, APL will output 'Nice' and 'The greatest?' but not 'Really?'
@Adám if the year is 1975?
Jan 31, 2018 18:50
If it isn't that wine, but the year is 1962 then you'll only get 'Really?' and 'The greatest?'
@EriktheOutgolfer Well, we can't see what comes below, but if it is just :EndIf, then nothing happens.
@EriktheOutgolfer Meaning, 'Nice' will be skipped because the first condition was false.
Btw, APL control structures are case insensitive, and (for golfing only!) you may omit what is being :Ended, i.e. you can use :End instead of :EndIf and :EndFor etc.
@Adám If I have something like this
:If X
    :For Y
        ...
    :EndFor
:EndIf
@EriktheOutgolfer Yes?
can I omit the :EndFor? now that would be golfing
@Adám ninja'd :P ^
@EriktheOutgolfer No, only the trailing "For".
Btw, you may branch () into and out of blocks, and APL will ignore unneeded endings.
@EriktheOutgolfer In this case it would be unambiguous, but what about:
:For X
    :For Y
        ...
    :EndFor
:EndFor
@Adám from where I come, something like this
:If X
    :For Y
        :For Z
            :If W
        :EndFor
:EndIf
would behave as I've indented
(like, it ends the inner-most such control structure)
Jan 31, 2018 18:58
@EriktheOutgolfer Yuck. No, you can't do that in APL.
@EriktheOutgolfer APL is not a golfing language.
@Adám no, it's not the behavior of a golfing language, it's how Python works
if x:
    for y:
        for z:
            if w:
@EriktheOutgolfer But Python doesn't need ends at all, does it? It uses indentation to indicate blocks, no?
^^python works like that, no ending
@Adám sometimes you want to end it early, and the only conversion I've done here is to use such endings to represent real endings instead
Jan 31, 2018 19:00
@betseg Well, since APL ignores whitespace (except between identifiers) it wouldn't work.
for example if you want to break a for or while loop in the middle you use break
basically, :End.* in APL is like } in C-family languages
and if you use break under an if under a while it will end the while loop instead
@EriktheOutgolfer Conversion?
and if you use return in a while loop in a defined function in Python, it will end the function altogether
now that is messed up
Jan 31, 2018 19:03
@EriktheOutgolfer In Dyalog APL, you can use :Leave to leave :For, :Repeat, and :While.
@EriktheOutgolfer :Return will leave the current function. It is the same as →0
:For X
    :If Y
:Leave
@EriktheOutgolfer Yeah, but I'd indent the :Leave to show that it is depending on the :If
Btw, the IDEs provide auto-indenting (and in general normalisation of whitespace).
I should also mention that :Continue starts the next iteration early in :For, :Repeat, and :While.
And now that we've already started, let's do :For.
It is a bit special, in that it has a secondary keyword: :For variable :In values
You may also use multiple variable names: :For var1 var2 :In values
But then you need to decide how to pair up the variables with the values. Observe:
⎕←⍎⎕fx'f' ':For a b c :In (1 2 3)(4 5 6)(7 8 9)' '⎕←a b c' ':EndFor'
@Adám
1 2 3
4 5 6
7 8 9
⍎VALUE ERROR
Jan 31, 2018 19:10
(don't worry about the value error)
Now I'll change :In to :InEach:
⎕←⍎⎕fx'f' ':For a b c :InEach (1 2 3)(4 5 6)(7 8 9)' '⎕←a b c' ':EndFor'
@Adám
1 4 7
2 5 8
3 6 9
⍎VALUE ERROR
it even has 2 secondary keywords
So with :In the variables iterated through the values together, while :InEach had each (!) variable iterate through its own value list.
Btw, in the true spirit of array programming, the values array may have any rank. So you can iterate through a matrix. It will be processed in ravel order.
@all clear about these?
@Adám ...hm, yeah
yep
Jan 31, 2018 19:18
Then we have :While/:Repeat
ngn
ngn
@Adám Will it iterate over empties like ¨ does (execute the body once with the prototypical element)?
@ngn No, it will not execute the block at all if the values' array has no elements.
 R←X F Y
 :For A B :InEach (0 1 2)⍬ ⍝ LENGTH ERROR
     ⎕←A,B
 :EndFor
 R←X+Y
()ed F)
@EriktheOutgolfer Because A will want to iterate thrice while B can't get any values.
 :For A B :InEach (0 1 2)(0 1) ⍝ LENGTH ERROR
     ⎕←A,B
 :EndFor
 R←X+Y
another case
Jan 31, 2018 19:23
@EriktheOutgolfer Compare with (0 1 2){A B←⍺ ⍵ ⋄ ⎕←A,B}¨(0 1)
Maybe it's just me, but I think it is pretty obvious why that shouldn't work. Same reason why 0 1 2+0 1 doesn't work.
@Adám just tryin' stuff out :)
OK.
Anyway, :While and :Repeat are really the same, just with and without a condition.
You can also have conditions at the end of these two.
:Repeat is like while(true) in C or loop in Ruby?
@betseg I guess so.
To not have any condition at the end, you use :EndWhile and EndRepeat.
To have a condition at the end, use :Until (but notice that this is in the negative!)
Just like with :If you can add :AndIf or :OrIf after :While and :Until (although I would advise you not to do that for :Until as it quickly becomes confusing):
[1]   :While 100>i
[2]   :AndIf 100>j
[3]       i j←foo i j
[4]   :Until 100<i+j
[5]   :OrIf i<0
[6]   :OrIf j<0
@Adám I find the [243] notation really wierd
Jan 31, 2018 19:34
@Uriel [243]?
I get why it was used traditionally
But not why it is used today
@Adám lines numbering, [1] j←π
@Uriel Oh, well you don't have to ever see or use it. I'm just copying from a text that has it.
in the IDE if you don't use )ed to edit the function you prefix every line by its number and then you press enter and that line of the function is modified
@Uriel Although it is really useful when referring people to locations in your code, or to look up where an error happened.
@EriktheOutgolfer That's the ∇-editor. You can also define functions with ⎕FX and ⎕FIX.
Wait, @Uriel are you only bothered by the notation or by the whole existence of line numbers?
Then we have :Select. It is also known in some languages as "Switch".
The following example really says it all:
[1]   :Select ?6 6
[2]   :Case 6 6
[3]       'Box Cars'
[4]   :Case 1 1
[5]       'Snake Eyes'
[6]   :CaseList 2⍴¨⍳6
[7]       'Pair'
[8]   :CaseList (⍳6),¨⌽⍳6
[9]       'Seven'
[10]  :Else
[11]      'Unlucky'
[12]  :EndSelect
If you put statements between :Select and the first case, they will be skipped.
Btw, if for some reason you don't like right-arrows, you can use :GoTo instead. I've no idea why we need both.
A really neat structure is the :Trap. After :Trap you can write one or more error/exception numbers (remember those from Lesson 13?). You can of course use 0 for all errors and 1000 for all exceptions.
If you just want to trap an error and continue, just close the trap structure with :EndTrap. Otherwise you can add case statements identical to those of :Select and APL will jump to the relevant handling block for that particular error:
[1]    :Trap 0 1000
[2]        Main ...
[3]    :Case 1002
[4]        'Interrupted ...'
[5]    :CaseList 1 10 72 76
[6]        'Not enough resources'
[7]    :CaseList 17+⍳20
[8]        'File System Problem'
[9]    :Else
[10]       'Unexpected Error'
[11]   :EndTrap
Leaving a trap block early cancels the handler.
Jan 31, 2018 19:50
in python you can do something like (copying from python docs):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     finally:
...         print("executing finally clause")
is there something like finally of else
(it always executes finally, executes else if except didnt catch anything)
@betseg Explain to me the difference from:
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print("division by zero!")
...     else:
...         print("result is", result)
...     print("executing finally clause")
nothing, just python ¯\_(ツ)_/¯
what betseg said, some stuff is, like, useless
(and java too i think)
however, in Python (2) you can do something like this:
for x in range(10):
    print x * 2 + 1
else:
    print 'loop ended successfully!'
Jan 31, 2018 19:56
@betseg I did overhear chatter about :Finally. The idea is that it is guaranteed to be executed even if the block is left early. A bit similar to a destructor in OO.
@EriktheOutgolfer What does that do? (different from just printing unindented and omitting else)
@Adám the indentation is fundamental in Python
@EriktheOutgolfer How is this different from:
@Adám the else clause only executes if the for loop ends normally without a break statement
for x in range(10):
    print x * 2 + 1
print 'loop ended successfully!'
Jan 31, 2018 19:58
@EriktheOutgolfer Ah, I see. It is like the opposite of Finally. Both run after the block terminates, but Else only if actually finished while FInally no-matter-what.
@Adám finally also executes if some other exception then what you're trying to catch is thrown
@Adám difference from this message
@betseg Right. Whenever the trap is left. Got it.
So Dyalog doesn't have any of those two yet, but may add it soon (big customer requested :Finally).
:EndLesson
 
Conversation ended Jan 31, 2018 at 20:00.