Sharpening your APL knife
Dynamic Functions
Dynamic Functions in general -1-
- Dynamic functions are created by putting code between curly brackets
- They are often called dfns or curlies
- They have a number of special properties that make them ideal for small to mid-sized functions
- Dynamic functions can be assigned a name
- They do not act on stop vectors
- The right argument has a fixed name: ⍵ (omega)
- The optional left argument has a fixed name: ⍺ (alpha)
- You cannot loop in a dfns
Note: There are also dynamic operators which are not covered by this course
Dynamic Functions in general -2-
Any :ControlWords must not appear in a dfns. As a consequence, a dfns cannot...
- have control structures like :For
- have conditions like :If
- be a method due to the lack of a :Public statement
Note: There are also dynamic operators which are not covered by this course
Dynamic Functions in general -3-
- Every variable created inside a dfns is local by definition
- Re-used names create a new variable (memory, edit window!)
- dfns can be called recursively with a special syntax (more effective than tfns)
- So-called recursive tail calls are even more effective
- Only multi-line dfns can be traced
- (dfns cannot act as callbacks) lifted!
- dfns stop on the first line with a result not assigned to a variable
- Note that many system functions return shy results
- Functions that do not return a result can be called in a dfns, but the dfns will return right after the call
Scope in tfns

In a tfns, a local variable is "visible" from a called function (semi-global)
Scope in dfns

In a dfns, a local variable is not visible from a called function
Simplest possible Dynamic Functions
The simplest possible dynamic function:
{}
- This function does not contain any code
- No name is assigned
- It simply gulps the right argument without any effect
- Can be used as a kind of trash for unwanted results
"Where" with dnfs
Where←{⍵/⍳⍴⍵}
- No temporary variable needed
- It is an idiom: fast!
Optional left argument
- Optional←{⍺←'Default' ⋄ ⍺}
- Optional ⍬
- Default
- 'Override' Optional ⍬
- Override
Guards -1-
Guards are the :If-aquivalent of dfns:
- Value←{
- ⍵<0:'Negative'
- ⍵>0:'Positive'
- 'Is Zero'
- }
Guards -2-
Use this technique to implement :if-:Else with dfns:
- LargeDfns←{
- value←{⍵<0:'Negative' ⋄ ⍵>0:'Positive' ⋄ 'Is Zero'}⍵
- ...
- }
Applications -1-
- Given a form "F" with...
- ...a ListView "lv" with 5 columns...
- ...one can adjust the column size according the data held in the cols by saying:
{F.lv.SetColSize ⍵ ¯3}¨⍳5
Try to do this without a dfns!
Dfns: Pros -1-
n←(⊃¨1⊃¨ind[2]⊃¨ip[b])(⊃¨2⊃¨ind[2]⊃¨ip[b])
- Given that this statement worked fine for quite a long time...
- ...and stops working one day with an error...
- ...one know that presumably the data is ill-formed...
- ...and the amount of data involved might be LARGE!
Dfns: Pros -2-
n←(⊃¨1⊃¨ind[2]⊃¨ip[b])(⊃¨2⊃¨ind[2]⊃¨ip[b])
- Developing such a statement without using the "Each" operator is supposed to be much easier
- That statement can then be assembled into a dfns which in turn is "eached" against the data
- Often you may find that using the dfns is faster
Dfns: Performance
Given a vtv with a total of one million characters, what is supposed to be the fastest solution?
- +/∨/¨' wiki'∘⍷¨buffer
- +/{∨/'wiki'⍷⍵}¨buffer
- :For this :In buffer ⋄ {}+/∨/'wiki'⍷this ⋄ :EndFor
See "#.dfnsPerformance.test" in the "Author" workspace
Dfns: Examples -1-
- path←'c:\MyDocs\Special'
- files←'one.html' 'two.jpeg' 'three.doc'
- (⊂path,'\'),¨files
- path∘{⍺,'\',⍵}¨files
- {⍵,'\',⍺}∘path¨files
Recursive calls
- A dfns can call itself recursively with a special syntax
- This special syntax is needed because a dnfs may or may not have a name
- The special syntax is also supposed to be faster
- recursive←{
- 1<≡⍵:∇¨⍵
- do_something ⍵
- }
Tail calls -1-
- When a Dynamic Function calls a sub-function, the result of the call may or may not be modified by the calling function before being returned
- A call where the result is passed back immediately without modification is termed a tail call
- (⍺×⍵)fact ⍵-1 ⍝ Tail call on fact.
- ⍵×fact ⍵-1 ⍝ Embedded call on fact.
Tail calls -2-
Tails Calls...
- are optimized
- are executed faster
- occupy much less memory
Demonstrate the "#.sieve.test*" functions in the "Author" workspace
Dfns: Variables
Every assignment in a dfns creates a new variable
Exceptions are:
- Indexed assigments
- Selective assignment
- Modified assignment
Don't forget this when dealing with very large variables!
Dfns: Cons
As everything else in APL, dfns can easily be abused
If misapplied, dfns can be...
- harder to read
- harder to maintain/debug
The arguments of dfns have fixed names: explain them in Detail if not absolutely clear from the context
Dfns: Advice
- When possible, avoid defining local multi-line dfns...
- ...because that makes editing during a trace session hard
- Avoid calling dfns in chains...
- ...because tracing can become a nightmare otherwise
- dfns1 dfns2 dfns3 dfns4 dfns4¨LARGE_Data
Dynamic Operators
Not in the scope of this course
End