Reputation: 16242
Consider a list of gerunds and some data we wish to apply them to, cyclically:
ms=.*`+`- NB. list of gerunds
d =.3 4 5 6 NB. some data
We can do:
ms/ d NB. returns 9, ie, the result of 3 * 4 + 5 - 6
Now we pose the question: how does the result change if we change the order in which we apply the verbs? That is, we consider all 6 possible orders:
allms=. (A.~i.@!@#) ms
which looks like:
┌─┬─┬─┐
│*│+│-│
├─┼─┼─┤
│*│-│+│
├─┼─┼─┤
│+│*│-│
├─┼─┼─┤
│+│-│*│
├─┼─┼─┤
│-│*│+│
├─┼─┼─┤
│-│+│*│
└─┴─┴─┘
To answer the question, we can do:
allms (4 : 'x/ y')"1 d
NB. returns 9 _21 _1 _23 _41 _31
But notice I was forced to use an anonymous, non-tacit verb to accomplish this. Because in order to apply the adverb /
, I had to have a named verb. When what I really wanted to do is treat /
like a rank 1 verb and "map" it over my list allms
, something in spirit like the illegal formulation:
/&d"1 allms NB. This is invalid J
That is, for each gerund on the list, transform it with the adverb /
and apply it to the data d
.
J seems to resist this higher-order "treating verbs like data" thinking. So I want to know what the natural J way of approaching this problem would be.
To be specific, you are given the list of gerunds ms
and data d
, as defined above. The task is to create a verb that returns a list of the results ms/ d
, for every possible ordering of ms
(ie, a verb that returns 9 _21 _1 _23 _41 _31
in our example). The verb must be a tacit verb.
Upvotes: 4
Views: 221
Reputation: 2324
There are fundamental syntactic reasons why you can't tacitly slice and dice arguments to operators (adverbs and conjunctions).
Without going into detail, allowing operators to be modified by other operators, like your proposed /
modified with "1
, would require a fundamental restructuring of J's grammar. And there would be major tradeoffs, in particular to simplicity and expressiveness (i.e. notational elegance)¹,².
So, if you want to distribute operators over gerunds like this, you'll have to write utilities for it, and the most straightforward way by far is by using explicit code. One pre-packaged utility to consider in this domain is the doog
script, available in the J Wiki and SVN repo.
However, the doog
script, like your approach, is fundamentally explicit³.
So if you really want to achieve these ends tacitly:
D =. foo`bar`baz
t =. D / (@:]) NB. Here's our "over" (/)
over =. [^:(D -: ]) L: (L.D) & (5!:1<,'t')
allOver =: (]^:[~ 1:`'' , over f.)~
3 4 5 6 allOver"1~ (A.~i.@!@#) *`+`- NB. Note the "1
9 _21 _1 _23 _41 _31
Without getting into too much detail, the trick here is using the verb ]^:[
to allow ^:
to execute an arbitrary atomic representation as input.
That is, some_atomic_rep f^:[ data
turns into f^:some_atomic_rep data
, which, for a suitable atomic rep, can execute anything at all, while using all the argument-processing goodness available to verbs (in particular, rank).
The rest is just an elegant (read: lazy) way to turn your gerundial inputs (whichever parts you make available to the verb with rank or other argument-selection mechanisms) into an atomic rep suitable for a right-hand argument to ^:
.
The meat of it is we have the template D / (@:])
and we replace D
with the gerund of your choice (the @:]
is necessary because by the time the gerund gets executed, it'll have two inputs: your actual input, d
, as well as itself, D
)4.
To visit the Ultima Thule of these wicked follies, check out the discovery of dont
in J, which is just like do
(".
), except ... really, you shouldn't.
¹ As a quick example: puzzle out what this would mean for precedence between wordclasses.
² Having said that, Jose "Pepe" Quintana, the leader of the underground J club F^4 (The Fully Fixable Functional Faction), once found a backdoor that actually did allow operators to take other operators as inputs. See this message in the "J Myths Puzzles" thread from 2008 (scroll past all the spoiler-hiding blank lines). Of course, once he mentioned it, Roger took notice, and immediately plugged the gap.
³ The way I once put it was "Yes, dcog is ugly, but I like to think of it as Messiah Code: it's ugly so that other code doesn't have to be. A sponge for sin".
4 Take note, the template gerund foo`bar`baz
can be anything you like, of any length, using any names. It doesn't matter. What does matter is that the names you use are either proverbs or undefined (which the interpreter treats like proverbs, by design). Using pronouns or pro-operators will break stuff. Alternatively, you could use another kind of noun, like simply __
or something (which I find mnemonic for fill in the ____).
Upvotes: 2
Reputation: 4302
ms=.*`+`- NB. list of gerunds
d =.3 4 5 6 NB. some data
allms=. (A.~i.@!@#) ms
I'd start by "treating my verbs like data" using strings to represent the gerunds
*`+`-
append the '/'
character and then use 128!:2
(Apply) which takes a string describing a verb as its left argument and applies it to the noun that is its right argument. of course to do this you need to make allms into verb strings.
That can be done using:
[ ger=. ,&'/' @ }: @ (1j1 #!.'`' ;)"1 allms
*`+`-/
*`-`+/
+`*`-/
+`-`*/
-`*`+/
-`+`*/
Then using 128!:2
(Apply)
ger 128!:2 d
9 _21 _1 _23 _41 _31
As a one line tacit verb
gvt=. ,&'/'@ }:@(1j1 #!.'`' ;)"1 @: [ 128!: 2 ]
allms gvt d
9 _21 _1 _23 _41 _31
I rarely play these games, so I am not saying this is the best approach, but it works.
Upvotes: 1