:Andif versus :Orif

General APL language issues

:Andif versus :Orif

Postby Tomas Gustafsson on Thu Apr 19, 2018 2:37 am

Hey guys, had a bit of confusion :-)

Code: Select all
     ∇ loop
[1]
[2]    :If 0
[3]        'aaa'
[4]    :OrIf 0
[5]        'bbb'
[6]    :End
[7]
[8]    →1
     ∇

Screen gets pumped with aaa's.

Code: Select all
     ∇ loop
[1]
[2]    :If 1
[3]        'aaa'
[4]    :OrIf 0
[5]        'bbb'
[6]    :End
[7]
[8]    →1
     ∇

Now gets filled with bbb's.

This feels a bit odd. I would not expect the aaa's in the first fn. In the 2nd, i understand that the bbb's come, as the "accumulated condition" just prior to is 1. But then why don't the aaa's come in that fn as well, *especially* as there is an :If 1 just above, and **especially** since they came after an :If 0?

I believe that the interpreter at an early stage resolves whether it is an :Andif or :Orif type of logic (at the same indent level ofc) and choses between two different ways?

Imho this is how it should work:
- If it's an :If/:Andif/:Andif... chain, it shoud execute anything in between until the first ":Andif 0".
- If it's an :If/:Orif/:Orif... thingy, it should execute in-betweens from the first ":Orif 1" onwards.

This is not the current behaviour, neither in v. 14 nor 16. I understand there is a (probably good) reason for how it does it now, and also that nobody in this world probably has the courage to alter this any longer. But it kinda feels that the evaluation is strange. Or maybe one step beyond?

https://www.youtube.com/watch?v=C9N8piRFVcU
Tomas Gustafsson
 
Posts: 91
Joined: Mon Sep 19, 2011 6:43 pm

Re: :Andif versus :Orif

Postby Phil Last on Thu Apr 19, 2018 6:34 am

In my article in Vector 22.1, November 2005, http://archive.vector.org.uk/art10004920, where I was showing how easy it is to avoid control structures entirely, I pointed out my especial disquiet about :OrIf, subsequently reread the documentation expecting it all to become clear - and gave up.
User avatar
Phil Last
 
Posts: 498
Joined: Thu Jun 18, 2009 6:29 pm

Re: :Andif versus :Orif

Postby Tomas Gustafsson on Thu Apr 19, 2018 9:47 am

Yes Phil, and all valid - for your case. But you assume an isolated condition test, with boundaries. In the speed-greedy environment i am forced to minimise execution ALL time, while otoh there are multiple things to do while advancing. Look at this:

Code: Select all
 #.TV.Input.GetKeyBuffer refKeyData refKeyNum
 :If 0≠a←refKeyNum.Value
     a←a⍴refKeyData.Value
     b←a.Pressed ⋄ (a.Released/b)←0
     K256[a.Key]←b
     k←b/a.Key ⋄ b/⍨←b
     :If focus
     :AndIf (~∨/SCA←K256[SHICONALT])∧∨/c←k∊reserved
         handlerLoop¨-c/k
         b∧←~c
     :End
     STATE←1+⊃1↑SCA/1 2 3 4 5 6
 :AndIf focus∧∨/b
     event←∊{
         keyTab[⍵[2];⍵[1]]
     }¨↓STATE,[1.1]b/k
     :If ∨/c←0≠event
         handlerLoop¨c/event
         stateUPD←1
     :End
 :End

or this:
Code: Select all
 :If 0≠⊃⍴a←⎕NL 9
     a⌿⍨←∨/'GC'⍷a[;1 2]
 :AndIf 0≠⊃⍴a
     ns←⍎¨↓a
     ns.Destroy
     Controllers.RemoveEx¨ns.Id
     Controllers.Clear
     ⎕EX'Controllers'
     ⎕EX¨⍕¨ns
 :End

or this:
Code: Select all
                 :If ns.Dist<1852
                     c←#.M.cpa(#.WORLD.ROOTPOS[1 3])(1 ¯1×#.GUI.compassRot[1 2]×#.VESSEL.myNS.linVelo)ns.Pos(-ns.arpaDelta[1 2])
                 :AndIf c[1]>¯10
                 :AndIf c[1]<120
                 :AndIf c[2]<926
                     ARPAcpa[i2]←1
                     d←arpaxform2 2 2⍴2↓c
                     ARPAcpa1[i2;]←p[1;],d[2;],#.GUI.Silver
                     ARPAcpa2[i2;]←(,d),#.GUI.Red
                     :If 0≠ARPAinfoslot[i2]
                         ARPAtext3[slot]←⊂'RCPA ',(0⍕2⊃c),'m TCPA',(0⍕1⊃c),'s'
                         draw2,←i2
                     :End
                 :Else
                     ARPAcpa[i2]←0
                 :End

or especially this:
Code: Select all
     :If 0≠⍴b
         a←∊2⍴¨+/¨b.((⌈/|fNormalSpeed fTangentSpeed)×fForce)
     :AndIf ∨/d←a≠0
         a/⍨←d
         c←d/∊b.(iBody1 iBody2)
         d←rkU_SLOT≥e←nsU_SLOT.Body⍳c
     :AndIf ∨/d←2/∧/(⌽2,0.5×⍴d)⍴d
         e/⍨←d ⋄ a/⍨←d
         ns2←nsU_SLOT[e]
         f←0⌈1⌊650000×a÷ns2.nsBoatClassData.Mass
     :AndIf ∨/a←f=1
         f/⍨←a ⋄ ns2/⍨←a
         :If ∧/a←ns2.nsBoatClassData.Class∊4 5 8 11 12
             b←⍋ns2.nsBoatClassData.Mass
             ns2←(⊃b)⊃ns2
             ns2.EFF×←0.99
         :End
     :End

and you can see why :Andif and :Orif are quite fit. I have them all over the place. The point in the sniplets above is to enable early bail out in cases where it's possible that it happens. To save time, have high-performance code in all situations (and keep the frame rate up). Sure some could be replaced with branching and dfn : but the control structures also provide a tremendous readability, writeability, codeability and nerdability!
Tomas Gustafsson
 
Posts: 91
Joined: Mon Sep 19, 2011 6:43 pm

Re: :Andif versus :Orif

Postby gil on Fri Apr 20, 2018 5:31 am

but the control structures also provide a tremendous readability, writeability, codeability and nerdability!


I think we can all agree that when it comes to :OrIf, this is not always true. I have to think more when I read blocks with OrIf to understand what it does and second guess the intention of the developer. The easiest way for me to parse it is to remove the semantics and think of it as branching (sort of defeats the purpose of control structures, but hey)


:If cond1
'aaa'
:OrIf cond2
'bbb'
:OrIf condN
'XXX'
:Endif

translates to

→ cond1 ⍴ blockN
'aaa'
→ cond2 ⍴ blockN
'bbb'
→ condN ⍴ blockN
→ end
blockN:
'XXX'
end:


Knowing that you can of course take advantage of how it works, but I don't think it is improving readability a lot.
gil
 
Posts: 60
Joined: Mon Feb 15, 2010 12:42 am

Re: :Andif versus :Orif

Postby Phil Last on Fri Apr 20, 2018 7:29 am

The reason I mentioned the artical was just to show that Tomas is not alone in thinking :OrIf a bit odd.

I do disagree about the readability of control structures in general however. In my view they emphasise trivialities and disourage array logic making code unneccesarily verbose.

But I see no use of :OrIf in Tomas's code. I guess his problem with it is the same as mine and I think it's probably down to the fact that :AndIf acts exactly like ∧\ (and scan) but :OrIf behaves exactly unlike ∨\ (or scan) while both scans are associative. See below for a demonstration of this.

We expect and are satisfied that :AndIf executes code while conditions continue to be true but we are misled into expecting :OrIf to refrain from executing anything until it finds a true condition.
      ⎕cr¨↓⎕nl 3
┌────────────┬────────────┐
│ r←AndIf w │ r←OrIf w │
│ r←'' │ r←'' │
│ :If 0⊃w │ :If 0⊃w │
│ r,←'a' │ r,←'a' │
│ :AndIf 1⊃w │ :OrIf 1⊃w │
│ r,←'b' │ r,←'b' │
│ :AndIf 2⊃w │ :OrIf 2⊃w │
│ r,←'c' │ r,←'c' │
│ :End │ :End │
│ r←'abc'∊r │ r←'abc'∊r │
└────────────┴────────────┘
(⊢,' ',AndIf⍤1,' ',∧\)⍉2 2 2⊤⍳8
0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0
0 1 1 0 0 0 0 0 0
1 0 0 1 0 0 1 0 0
1 0 1 1 0 0 1 0 0
1 1 0 1 1 0 1 1 0
1 1 1 1 1 1 1 1 1
(⊢,' ',OrIf⍤1,' ',∨\)⍉2 2 2⊤⍳8
0 0 0 1 1 0 0 0 0
0 0 1 1 1 1 0 0 1
0 1 0 1 0 1 0 1 1
0 1 1 1 0 1 0 1 1
1 0 0 0 0 1 1 1 1
1 0 1 0 0 1 1 1 1
1 1 0 0 0 1 1 1 1
1 1 1 0 0 1 1 1 1
User avatar
Phil Last
 
Posts: 498
Joined: Thu Jun 18, 2009 6:29 pm

Re: :Andif versus :Orif

Postby Tomas Gustafsson on Fri Apr 20, 2018 9:06 am

Exacly, Phil, :Orif does not "work" and hence does not appear in code. I have over 10x more functions containing :Andif than :Orif. The :Orif's are always on top of each other, with no real structuring management - could as well be written in one sentence, so it's more about maintaining documentability for my own mind.

Here's an interesting one, that naturally COULD be compressed into something more compact (though i'm not at all sure it would be faster, as simple things are fastest in APL):

Code: Select all
 CURSOR←0
 :If ∨/BNup              ⍝ Important: My point with splitting up these is
 :OrIf ∨/BN              ⍝ that it i want to avoid having to evaluate
 :OrIf 0≠ROLL-oROLL       ⍝ all three!
     ROLLED←⊃×ROLL-oROLL  ⍝ Instead eg. the 1st (most likey) :If causes a jump here!
     :If #.SYS.isMenu
     :AndIf ∨/3↑#.INP.BNup
     :AndIf ∧/(∧/#.INP.XY≥#.MENU.surfOrig)∧#.INP.XY≤#.MENU.(surfOrig+surfSize)
         CURSOR←1
     :ElseIf #.SYS.Info[1]
     :AndIf ∨/2↑BNup
     :AndIf ∨/(∧/#.INP.XY≥[2]2↑[2]#.SYS.hitHUDMain)∧∧/#.INP.XY≤[2]0 2↓#.SYS.hitHUDMain
         CURSOR←2
     :ElseIf #.SYS.isBino
         CURSOR←3
     :ElseIf #.SYS.isChartSel
         CURSOR←4
     :ElseIf #.SYS.isRadar
     :AndIf (∧/XY≤#.BOAT.RADAR.screenPos[3 4])∧∧/XY≥#.BOAT.RADAR.screenPos[1 2]
         CURSOR←5
     :ElseIf #.SYS.isChart
     :AndIf ∧/(XY≥#.BOAT.CHART.cScreen[1 2]),XY≤#.BOAT.CHART.cScreen[3 4]
         CURSOR←6
     :ElseIf #.SYS.isKvirtual
     :AndIf 0≠ROLLED
         #.GUI.shaderCompas..blablatoolong..←0⌈1⌊#.SYS.compassAlpha+0.05×ROLLED)
         ROLLED←0 ⋄ oROLL←ROLL
         CURSOR←7
     :End
 :ElseIf #.SYS.isChart
 :AndIf ∧/(XY≥#.BOAT.CHART.cScreen[1 2]),XY≤#.BOAT.CHART.cScreen[3 4]
     CURSOR←6
 :End

(It's ofc about allocating mouse actions to logical pieces of the screen depending on cursor position. A can also see room for a small improvement at top)

Tjänare Gil :-). I see your point, but the branching you wrote is imo not quite ok. I wouldn't want to jump to blockN, but to the next statement containing ->. Long ago we had these rather complex and messy collections or branching in code, and they were rather hard to read. Plus that branching was said to be slow. For one thng, they lack the indent. You'd have to use:

Code: Select all
→(condition)/⎕LC+2

For me, a relevant point with :Orif is (would be) that "I can do with the existing value of the (global) variable, but if :Orif proposes so, the code would update it right after - in any case i'll use that (old or updated) value at the end of the control structure". But that's not possible atm, as :Orif imho behaves wrongly.

Wondering if Dyalog has a hidden reason for this current behaviour, what's the story?

(Also, for my part, go ahead and change! :-))
Tomas Gustafsson
 
Posts: 91
Joined: Mon Sep 19, 2011 6:43 pm

Re: :Andif versus :Orif

Postby ray on Fri Apr 20, 2018 1:41 pm

Although (for readability) I do use IF statements a lot, I rarely use AndIf and never use OrIf (as they confuse me).

Often a set of OrIf can be simply replaced by a Select/Case/Castlist/Else control structure, which I find much simpler to read.

Recently, I was re-factoring some code which was making decisions of what action a robot should take, based on 3 boolean values.

The original code used control structures with embedded If/Else statements. Once having re-factored the code as a select/case control structure, the clarity of the "Case" statements allowed me re-factor again.
This time I replace the whole control structure with a simple single assign with indexing!
(The 3 boolean values were combined to form an integer from the 3 bits, with a value from zero to seven, and that value was then used to index into 8 element vector of "possible answers".)

From a "Code Golf" point of view, the "indexing" was shortest and easy to understand the mechanics of the code, but not the logic of why.
However, the Select/Case control structure showed how each of the 3 boolean values contributed to the answer, which the "indexing" had completely hidden.

Finally I reached a compromise, using the "indexing" in the code, but with comments describing the logic via a case statement.
Ray Cannon
Please excuse any smelling pisstakes.
User avatar
ray
 
Posts: 151
Joined: Wed Feb 24, 2010 12:24 am
Location: Blackwater, Camberley. UK

Re: :Andif versus :Orif

Postby kai on Fri Apr 20, 2018 2:17 pm

There are applications for :OrIf:

1. If some checks are expensive (time consuming) order them from cheap to expensive; the expensive ones will never be executed when the cheapest one fails.

2. If the subsequent conditions can only be executed at all when the first condition is false.

We expect and are satisfied that :AndIf executes code while conditions continue to be true but we are misled into expecting :OrIf to refrain from executing anything until it finds a true condition.


Code between the :OrIfs is executed because it has nothing to do with the check. If you want code to be executed only in case at least the last :OrIf is true then put it after the last one and everything is fine.

Putting code between :OrIfs makes rarely sense and should only be done to prepare something for the next :OrIf
User avatar
kai
 
Posts: 100
Joined: Thu Jun 18, 2009 5:10 pm
Location: Stevenage, UK

Re: :Andif versus :Orif

Postby Phil Last on Fri Apr 20, 2018 2:41 pm

kai wrote:Putting code between :OrIfs makes rarely sense and should only be done to prepare something for the next :OrIf
Thank you Kai. I'd very much like to see your explanation integrated into the existing help.
User avatar
Phil Last
 
Posts: 498
Joined: Thu Jun 18, 2009 6:29 pm

Re: :Andif versus :Orif

Postby Tomas Gustafsson on Fri Apr 20, 2018 7:59 pm

kai wrote:Putting code between :OrIfs makes rarely sense and should only be done to prepare something for the next :OrIf

Do remember that my app is in an eternal loop! It is about load balancing and working on persistent data. Here's to consider:

Code: Select all
 LoopCycle
⍝ x,y,Must_Update_x are globals
 :If Must_Update_x
     x←[time consuming thing]
 :OrIf x∧y←[other time consuming thing]
     [update Must_Update_x]
 :End

Here's how i'd like it to work:
Code: Select all
 huhuh
 :If 0
     No
 :OrIf 0
     No
 :OrIf 1
     Yes
 :OrIf 0 ⍝ (not evaluated)
     Yes
 :OrIf 0 ⍝ (not evaluated)
     Yes
 :End

Having it like this might first look sensible, but no in-betweens would ever be executed and hence would be redundant:
Code: Select all
 huhuh
 :If 0
     No
 :OrIf 0
     No
 :OrIf 1
     No
 :OrIf 0 ⍝ (not evaluated)
     No
 :OrIf 0 ⍝ (not evaluated)
     Yes
 :End

Having it like this might be a alternative, but it doesn't really feel right:
Code: Select all
 huhuh
 :If 0
     No
 :OrIf 0
     No
 :OrIf 1
     Yes
 :OrIf 0 ⍝ (not evaluated)
     No
 :OrIf 0 ⍝ (not evaluated)
     Yes
 :End

But the current behaviour is this:
Code: Select all
 huhuh
 :If 0
     Yes
 :OrIf 0
     Yes
 :OrIf 1
     No
 :OrIf 0
     No
 :OrIf 0
     Yes
 :End

I may understand the reason for this, it simply jumps to :End - 1 (or say to 2nd-last :Orif + 1) as soon as possible (at first 1), executing everything, both conditions and code in between before that.

And in the case of :Andif, it simply jumps to :End at earliest possible stage (at first 0), again executing both conditions and code in between before that.

That is understanable manufacturer-wise, but user/logic-wise it renders :Orif pretty much meaningless, into a simple boolean ackumulator (which :Andif indeed is too). My HEAD tells me that it should be:

- :Andif stops at 0
- :Orif starts at 1

And for BOTH it would be true that any further :Andif's or :Orifs were omitted.

Halleluja
Tomas Gustafsson
 
Posts: 91
Joined: Mon Sep 19, 2011 6:43 pm

Next

Return to Language

Who is online

Users browsing this forum: No registered users and 1 guest