Ian Martin
Ian Martin

Reputation: 95

Creating a vector of closures in Dyalog

I would like to define a vector of anonymous functions that take a single parameter, then map a parameter x across this vector, returning an array of the results of f(x) for each index. Is this possible in Dyalog?

I ask because my naive attempt at creating such a closure vector:

fs ← {⍵×2} {⍵×4} {⍵×8}

behaves strangely. Attempting to reference a single index of fs seemingly references all of them and garbles the output:

 fs[1]
   ∇{⍵×2}
 ∇       ∇{⍵×4}
 ∇       ∇{⍵×8}
 ∇  [ 1

⍴fs returns similar output rather than 3, leading me to believe I haven't made an array at all. What would be the correct way to go about this?

Upvotes: 5

Views: 274

Answers (3)

Adám
Adám

Reputation: 7616

The dfns workspace (which ships with Dyalog APL) discusses Function Arrays, and provides multiple methods to deal with such.

Alternatively, you can use the following monadic operator which as operand takes an array of one-liner function definitions and/or names (the named functions may be multi-line) and returns a function array which can then be applied:

 r←{x}(fns Fns)y;nss;ns
 nss←{⎕NS ⍬}¨fns
 ns←'.(',⍨⍕⎕THIS
 nss.⍎'f←'∘,¨ns∘,¨fns,¨')'
 :If 900⌶⍬
     x←⊢
 :EndIf
 r←x nss.f y

Try it online!

Upvotes: 2

Morten Kromberg
Morten Kromberg

Reputation: 788

An alternative way to create a structure which approximates an array of functions in Dyalog APL is to create an array of namespaces containing the functions. For example:

    nss←⎕NS¨'' '' '' 
    nss.⍎ 'f←{⍵×2}' 'f←{⍵×4}' 'f←{2+⍵×3}' 
    nss.f 11 
22 44 35

    nss[2].f 2
8

Upvotes: 4

EKons
EKons

Reputation: 927

Unfortunately, this is not possible in Dyalog. What you have made there is indeed not an array, an fgh-fork, which is a train of functions f g h that, when called monadically like (f g h)Y, behaves like (f Y)g h Y. If there is a function to the exact left of the left bracket ([) instead of an array, then this is not bracketed indexing, but the axis operator. However, the axis operator can only be applied to some primitive functions, as well as functions derived from /, \, or , so it will SYNTAX ERROR if applied to your function like that.

⍴fs is, again, a function train, in this case an atop, and not something that will return the shape of fs, as fs is a function.

However, even though functions are not first-class objects in APL, you can abuse trains in a way that makes it look like the parameter is actually mapped across an array of functions. Let's define a "Function Chain Link" function FL for this purpose:

FL ← {(⊂⍺),⍵}

So, suppose that we how have functions f, g, h and an argument Y and we want to return (f Y)(g Y)(h Y). This can now be written as (f FL g FL h)Y, right? Well, no, there is a catch. The right-most occurrence of FL must be replaced with FL∘⊂. Unfortunately, this is an unavoidable nuisance, but at least now you can be free from mentioning Y every time. Now there are two cases:

  • The array of functions (chain) has length 1, i.e. it only contains one function f: In this case, you can simply use (⊂f)Y.
  • The function chain contains multiple functions, let's suppose f, g and h: Now, you will write (f FL g FL∘⊂h)Y.

I recommend keeping the name of this function short, up to 2 characters, since it will be used many times.

And, how does that FL function work? In fact, it chains the results together, not the functions themselves. It simply encloses the result to its left in an array ((⊂⍺)), then concatenates the result to its right to it (,⍵). This results in "prepending" the left result to the array of results to the right, but keeping it together, i.e. not separating its individual elements. The reason is needed the first time it's used in such a "chain" is because FL can't determine if the array to its right is the result of the right function or an array of results, so, by enclosing the first time, you ensure that FL's right argument is always an array of results, and not a bare result that will be broken by prepending elements to it.

It's worth noting that you can call such a chain dyadically too, in which case all of its functions (not FL) will be called dyadically with the same two arguments. For example, X(f FL g FL∘⊂h)Y is the same as (X f Y)(X g Y)(X h Y). However, you may want to call some of the functions monadically. While not directly related to the problem at hand, this monadic operator will make functions ignore their left argument, if any, and is very helpful for that purpose, simply because you avoid parentheses:

M ← {⍺⍺ ⍵}

So, let's suppose we have four functions f, g, h and p, and we want to call g and p monadically, while keeping the left argument for f and h. Now that we have implemented M, we can do this, with X being the left argument and Y being the right:

X(f FL g M FL h FL∘⊂p M)Y

Again, I recommend that, if you decide to implement this monadic operator, you should keep its name up to 1 character long.

Upvotes: 5

Related Questions