How to calculate f1(f2(...fn(x)...)) given a gerund? - j

One can calculate f1(f2(f3(x))) as
*: +: >: 4 NB. 100
Given a gerund of "unknown" length (eg. m1 =: *:`+:`>:, or m2 =: +:`>:) how can one calculate f1(f2(...(fn(x)) ...))?
So far I have only been able to evoke the gerund using m `: 0.
(*:`+:`>: `: 0) 4 NB. 16 8 5

Let:
NB. an example gerund
] g=: *:`<:`+:`>:
┌──┬──┬──┬──┐
│*:│<:│+:│>:│
└──┴──┴──┴──┘
NB. a sentence to evoke
*: <: +: >: 4
81
1st approach (via dynamic programming)
1A. Compose the whole sentence then evoke it
Convert all elements in gerund to linear representation (LR), compose the sentence, then evoke it:
NB. utility to find out a LR from an atomic
NB. representation (AR) and parenthesize it
NB. pLR=. plr AR
plr=: 3 : 0
v=. y 5!:0
'(' , (5!:5 < 'v') , ') '
)
NB. result=. gerund v1 input
v1=: 4 : '". (; <#plr"0 x) , ": y'
NB. evoke v1 with a gerund and input applied
g v1 4
81
Pros: no
Cons:
input data type should be exact (neither float nor complex) since Format (":) may truncate
a verb applied to input can be a monad only
1B. Evoke component verbs sequentially
Take the current element from gerund, transform it to verb, apply that verb to accumulator, repeat for the next element from gerund:
NB. result=. gerund v2 input
v2=: >#(4 : '(x 5!:0)&.> y'/)#(, <)
NB. evoke v2 with a gerund and input applied
g v2 4
81
Pros:
any data types for input are allowed
Cons:
a verb applied to input can be a monad only
2nd approach (functional)
2A. Transform gerund to fork
NB. a fork we are going to compose, in action
([: *: [: <: [: +: >:) 4
81
NB. its structure
([: *: [: <: [: +: >:)
┌──┬──┬──────────────────┐
│[:│*:│┌──┬──┬──────────┐│
│ │ ││[:│<:│┌──┬──┬──┐││
│ │ ││ │ ││[:│+:│>:│││
│ │ ││ │ │└──┴──┴──┘││
│ │ │└──┴──┴──────────┘│
└──┴──┴──────────────────┘
NB. how to compose it
('[:' ; g)#.0 1 0 2 0 3 4
┌──┬──┬──────────────────┐
│[:│*:│┌──┬──┬──────────┐│
│ │ ││[:│<:│┌──┬──┬──┐││
│ │ ││ │ ││[:│+:│>:│││
│ │ ││ │ │└──┴──┴──┘││
│ │ │└──┴──┴──────────┘│
└──┴──┴──────────────────┘
NB. selector (ISO generator)
NB. iso=. sel gerund
sel=: (< < < _2) { (0 ,#,. #\)
NB. let's test it
sel g
0 1 0 2 0 3 4
NB. sentence to assemble a fork
('[:' ; g)#.(sel g)
┌──┬──┬──────────────────┐
│[:│*:│┌──┬──┬──────────┐│
│ │ ││[:│<:│┌──┬──┬──┐││
│ │ ││ │ ││[:│+:│>:│││
│ │ ││ │ │└──┴──┴──┘││
│ │ │└──┴──┴──────────┘│
└──┴──┴──────────────────┘
NB. an adverb executing that sentence
NB. fork=. gerund a2a
a2a=: 1 : '(''[:'' ; m)#.(sel m)'
NB. evoke that adverb to transform a gerund to a fork
g a2a
┌──┬──┬──────────────────┐
│[:│*:│┌──┬──┬──────────┐│
│ │ ││[:│<:│┌──┬──┬──┐││
│ │ ││ │ ││[:│+:│>:│││
│ │ ││ │ │└──┴──┴──┘││
│ │ │└──┴──┴──────────┘│
└──┴──┴──────────────────┘
NB. apply a fork produced to input
g a2a 4
81
Pros:
any data types for input are allowed
a verb applied to input can be of any valence (either monad, dyad or ambivalent)
Cons: no
2B. Transform gerund to conveyor
Join component verbs by At (#:) somehow. A resulting verb will be an At-chained sequence of component verbs.
2B1. Via arconj utility
Join component verbs to compose AR, then convert AR to verb by Define (5!:0).
NB. a chain we are going to compose, in action
*:#:<:#:+:#:>: 4
81
NB. its structure
*:#:<:#:+:#:>:
┌──────────────────┬──┬──┐
│┌──────────┬──┬──┐│#:│>:│
││┌──┬──┬──┐│#:│+:││ │ │
│││*:│#:│<:││ │ ││ │ │
││└──┴──┴──┘│ │ ││ │ │
│└──────────┴──┴──┘│ │ │
└──────────────────┴──┴──┘
NB. its AR
chain=. *:#:<:#:+:#:>:
5!:1 < 'chain'
┌────────────────────────────────┐
│┌──┬───────────────────────────┐│
││#:│┌──────────────────────┬──┐││
││ ││┌──┬─────────────────┐│>:│││
││ │││#:│┌────────────┬──┐││ │││
││ │││ ││┌──┬───────┐│+:│││ │││
││ │││ │││#:│┌──┬──┐││ │││ │││
││ │││ │││ ││*:│<:│││ │││ │││
││ │││ │││ │└──┴──┘││ │││ │││
││ │││ ││└──┴───────┘│ │││ │││
││ │││ │└────────────┴──┘││ │││
││ ││└──┴─────────────────┘│ │││
││ │└──────────────────────┴──┘││
│└──┴───────────────────────────┘│
└────────────────────────────────┘
NB. we'll compose it with AR utility (arconj) from addon
load 'misc/miscutils/langexten'
NB. an adverb assembling a chain
NB. chain=. gerund a2b1a
a2b1a=: 1 : '(''#:'' arconj~/ |. m) 5!:0'
NB. evoke that adverb to transform a gerund to chain
g a2b1a
┌──────────────────┬──┬──┐
│┌──────────┬──┬──┐│#:│>:│
││┌──┬──┬──┐│#:│+:││ │ │
│││*:│#:│<:││ │ ││ │ │
││└──┴──┴──┘│ │ ││ │ │
│└──────────┴──┴──┘│ │ │
└──────────────────┴──┴──┘
NB. apply a chain produced to input
g a2b1a 4
81
Pros:
any data types for input are allowed
a verb applied to input can be of any valence (either monad, dyad or ambivalent)
Cons: no
Notes:
a2b1a can be simplified to produce a chain with a slightly different structure but the same functionality:
a2b1b=: 1 : '(''#:'' arconj/ m) 5!:0'
arconj utility may be re-implemented manually as [1]:
arconj2=: (<'#:') ,#<#, <#,
a2b1c=: 1 : '({. arconj2/ m) 5!:0'
or as [1]:
arconj3=: 4 : '(x`:6)#(y`:6)`'''''
a2b1d=: 1 : '({. arconj3/ m) 5!:0'
2B2. Via Train (`:6)
Join component verbs, then apply Train (`:6) [2].
NB. a chain we are going to compose, in action
*:#:<:#:+:#:>: 4
81
NB. its structure
*:#:<:#:+:#:>:
┌──────────────────┬──┬──┐
│┌──────────┬──┬──┐│#:│>:│
││┌──┬──┬──┐│#:│+:││ │ │
│││*:│#:│<:││ │ ││ │ │
││└──┴──┴──┘│ │ ││ │ │
│└──────────┴──┴──┘│ │ │
└──────────────────┴──┴──┘
NB. an adverb assembling a chain
NB. chain=. gerund a2b2
a2b2=: 1 : '(}: , m ,. <''#:'')`:6'
NB. evoke that adverb to transform a gerund to chain
g a2b2
┌──────────────────┬──┬──┐
│┌──────────┬──┬──┐│#:│>:│
││┌──┬──┬──┐│#:│+:││ │ │
│││*:│#:│<:││ │ ││ │ │
││└──┴──┴──┘│ │ ││ │ │
│└──────────┴──┴──┘│ │ │
└──────────────────┴──┴──┘
NB. apply a chain produced to input
g a2b2 4
81
Pros:
any data types for input are allowed
a verb applied to input can be of any valence (either monad, dyad or ambivalent)
the simplest version
Cons: no
Notes
J Forums are the best place to ask if you prefer to get answer quicker.
References
[1]: [Jprogramming] Gerund composed application
by Raul Miller, 2017-09-25
[2]: System/Interpreter/Requests#verb pipelines by Dan Bron, 2007-12-21

This solution is not elegant, but it works.
Start by converting the gerund into a string form using Foreign Conjunctions Define (5!:0) adverb and Atomic Representation (5!:1) then unbox using Raze (;).
Then convert the y argument to a string using Default Format (":) and prepending a blank to give space to the gerund string.
Use Append (,) to create one string and apply Do (".) to that string for the result.
g=: 4 : 0
s=: ; (5!:1 <'t'[t=.x) 5!:0 NB. changes gerunds to string
a=:' ' , ": y NB. makes argument into a string prefixed by blank
". s,a
)
or on one line
g1=: 4 : ' ". (; (5!:1 <''t''[t=.x) 5!:0 ), '' '' , ": y'
*:` +:` >: g 4
100
*:` +:` >: g1 4
100

Related

REMOVE DUPLICATES from lINKED LIST. How come this code works and this one doesn't?

#This work despite not being returned cur but linkedlist in removeDuplicatesFromLinkedList
class LinkedList:
def __init__(self, value):
self.value = value
self.next = None
def removeDuplicatesFromLinkedList(linkedList):
cur = linkedList
while cur.next is not None:
if cur.value == cur.next.value:
cur.next = cur.next.next
else:
cur = cur.next
return linkedList
#This doesn't work
class LinkedList:
def __init__(self, value):
self.value = value
self.next = None
def removeDuplicatesFromLinkedList(linkedList):
while linkedList.next is not None:
if linkedList.value == linkedList.next.value:
linkedList.next = linkedList.next.next
else:
linkedList = linkedList.next
return linkedList
I would have expected in my code the second one to be able to get a linkedList without duplicates , but above all what amazes me is that in the first one it is not returned cur , and it still works I don't understand therefore where the change occurs
If the caller expects to get the linked list as a return value from the function, then indeed the second version will not work, as that function will always return a list with just one node in it -- the last node.
Initially, linkedlist references the head node of your linked list, and when potentially some duplicate-valued nodes are removed from that list, it should still reference the first node of your linked list. So in short, your code should not assign a different reference to linkedlist and then return that.
It may help to visualise this. Let's take an example list that has 3 nodes and has no duplicates: 1, 2, and 3. Then at the start of the good function, the state is like this:
cur
↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│ data: 1 │ │ data: 2 │ │ data: 3 │
│ next: ──────► │ next: ──────► │ next: null│
└───────────┘ └───────────┘ └───────────┘
↑
linkedList
So both linkedList and cur reference the same node just before the loop starts. As there are no duplicate values, in each iteration we'll execute the else part, and so after the first execution of cur = cur.next we get:
cur
↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│ data: 1 │ │ data: 2 │ │ data: 3 │
│ next: ──────► │ next: ──────► │ next: null│
└───────────┘ └───────────┘ └───────────┘
↑
linkedList
The while condition is still true, so a next iteration is executed:
cur
↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│ data: 1 │ │ data: 2 │ │ data: 3 │
│ next: ──────► │ next: ──────► │ next: null│
└───────────┘ └───────────┘ └───────────┘
↑
linkedList
At this point the while condition is false, and we're ready to return a reference to the linked list. It is clear that if we would return cur now, the caller would only have access to that node, and no longer to the first two nodes. It really is necessary to return the reference to the first node, i.e. we must return linkedList.
Note that it is of little importance whether nodes were deleted in the process or not. Either way, the loop will end when cur references the last node in the list, and linkedList still references the first node in the list. The caller is interested in receiving a reference to the first node, as otherwise they have no way to have access to all nodes of that list.
In the wrong version of the code, it will be linkedList that "walks" to the right -- just like cur did in the above process, and then return linkedList will not be helpful for the caller for the reasons explained above. In that version the last state is this one:
┌───────────┐ ┌───────────┐ ┌───────────┐
│ data: 1 │ │ data: 2 │ │ data: 3 │
│ next: ──────► │ next: ──────► │ next: null│
└───────────┘ └───────────┘ └───────────┘
↑
linkedList
This linkedList variable can not be used to reach to the first node in the list. There is no way to get there via next references. linkedList can only "see" one node, and so it represents a list with just one node.
On a final note: if the caller of removeDuplicatesFromLinkedList ignores the returned reference, and just continues to work with the reference they passed as argument to the function, then the caller will have no problem. Their reference is still to the first node of the list, and they can still iterate it and find that the duplicates have been removed.

Replace multiple strings with multiple values in Julia

In Python pandas you can pass a dictionary to df.replace in order to replace every matching key with its corresponding value. I use this feature a lot to replace word abbreviations in Spanish that mess up sentence tokenizers.
Is there something similar in Julia? Or even better, so that I (and future users) may learn from the experience, any ideas in how to implement such a function in Julia's beautiful and performant syntax?
Thank you!
Edit: Adding an example as requested
Input:
julia> DataFrames.DataFrame(Dict("A" => ["This is an ex.", "This is a samp.", "This is a samp. of an ex."]))
3×1 DataFrame
Row │ A
│ String
─────┼────────────────────
1 │ This is an ex.
2 │ This is a samp.
3 │ This is a samp. of an ex.
Desired output:
3×1 DataFrame
Row │ A
│ String
─────┼────────────────────
1 │ This is an example
2 │ This is a sample
3 │ This is a sample of an example
In Julia the function for this is also replace. It takes a collection and replaces elements in it. The simplest form is:
julia> x = ["a", "ab", "ac", "b", "bc", "bd"]
6-element Vector{String}:
"a"
"ab"
"ac"
"b"
"bc"
"bd"
julia> replace(x, "a" => "aa", "b" => "bb")
6-element Vector{String}:
"aa"
"ab"
"ac"
"bb"
"bc"
"bd"
If you have more complex replace pattern you can pass a function that does the replacement:
julia> replace(x) do s
length(s) == 1 ? s^2 : s
end
6-element Vector{String}:
"aa"
"ab"
"ac"
"bb"
"bc"
"bd"
There is also replace! that does the same in-place.
Is this what you wanted?
EDIT
Replacement of substrings in a vector of strings:
julia> df = DataFrame("A" => ["This is an ex.", "This is a samp.", "This is a samp. of an ex."])
3×1 DataFrame
Row │ A
│ String
─────┼───────────────────────────
1 │ This is an ex.
2 │ This is a samp.
3 │ This is a samp. of an ex.
julia> df.A .= replace.(df.A, "ex." => "example", "samp." => "sample")
3-element Vector{String}:
"This is an example"
"This is a sample"
"This is a sample of an example"
Note two things:
you do not need to pass Dict to DataFrame constructor. It is enough to just pass pairs.
In assignment I used .= not =, which perfoms an in-place replacement of updated values in the already existing vector (I show it for a comparison to what #Sundar R proposed in a comment which is an alternative that allocates a new vector; the difference probably does not matter much in your case but I just wanted to show you both syntaxes).

Elegant way in Haskell to output Fibonacci numbers using zipWith [duplicate]

This question already has answers here:
Understanding recursive fibonacci function in Haskell
(1 answer)
Haskell infinite recursion
(2 answers)
Haskell Fibonacci Explanation
(2 answers)
Closed 3 years ago.
I saw this implementation of the Fibonacci numbers in Haskell and I am still trying to figure out why this works properly. So apperently, the Fibonacci numbers can be written in a very compact way using the zipWith function. The implementation looks like the following
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
To understand better what is happening here I looked at the documentation of the zipWith function. This function adds two lists [a], [b] together using the given function (a -> b -> c). In our case the function is a simple addition. If the two lists [a] and [b] have different length (in our case list [b] is always one element shorted than list [a]) zipWith simply starts at the beginning of both lists and adds them. If the end of one list is reached it just stops no matter if the end of the other list is already reached.
In the first step of the recursion zipWith is called with [0,1] and tail[0,1] = [1]. This leads to another 1 => [0, 1, 1]. In the second step of the recursion zipWith gets called with [0,1,1] and [1,1] resulting in [0+1,1+1] = [1,2]. So for me it is clear that the recursion creates the right fibonacci numbers but I don't fully understand why only the last number after the zipWith step is added to the result and not the whole list. Maybe someone can explain it to me. That would be super helpful. Thank you very much.
The entire list is added eventually. In Haskell you do not assign a value to a variable. You declare a variable, and you can not change the value of a variable since these are immutable. The list is thus not [0, 1], it is [0, 1, …], with … something that is at that moment not evaluated yet.
Initially the list thus looks like:
┌───────────────────────────────────┐
│ ┌─────────────────────┐ │
▼ ▼ | │
┌---┼---┐ ┌---┼---┐ ┌-----------┐ | │
| ∙ | ∙────▶| ∙ | ∙────▶| zipWith | | │
└-│-┴---┘ └-│-┴---┘ ├-----------┤ | │
▼ ▼ |(+)| ∙ | ∙ | | │
0 1 └-----│---│-┘ | │
│ └───┘ │
└─────────┘
If you later decide to evaluate the next item of the list, zipWith will calculate the sum of the heads of the lists to which it refers, so [0,1,…] and [1,…], which is one. It will thus emit 1, and recurse on the tails of the two lists:
┌───────────────────────────────────┐
│ ┌─────────────────────┐ │
▼ ▼ | │
┌---┬---┐ ┌---┼---┐ ┌---┼---┐ ┌-----------┐ | │
| ∙ | ∙────▶| ∙ | ∙────▶| ∙ | ∙────▶| zipWith | | │
└-│-┴---┘ └-│-┴---┘ └-│-┴---┘ ├-----------┤ | │
▼ ▼ ▼ |(+)| ∙ | ∙ | | │
0 1 1 └-----│---│-┘ | │
│ └───┘ │
└─────────┘
So now the list is [0,1,1,…]. If we then force the system to evaluate the next item, it will again sum up the heads of the lists, so [1,1,…] and [1,…], which is 2:
┌───────────────────────────────────┐
│ ┌─────────────────────┐ │
▼ ▼ | │
┌---┬---┐ ┌---┬---┐ ┌---┼---┐ ┌---┼---┐ ┌-----------┐ | │
| ∙ | ∙────▶| ∙ | ∙────▶| ∙ | ∙────▶| ∙ | ∙────▶| zipWith | | │
└-│-┴---┘ └-│-┴---┘ └-│-┴---┘ └-│-┴---┘ ├-----------┤ | │
▼ ▼ ▼ ▼ |(+)| ∙ | ∙ | | │
0 1 1 2 └-----│---│-┘ | │
│ └───┘ │
└─────────┘
and so on. The list is thus an infinite list, but the tail is evaluated lazily.
The lists don’t have changing lengths. The length is always infinity, we just don’t know about most of the elements.
First recall that the following are equivalent:
[1,2,3]
1:2:3:[]
In this case we will write ... for the tail of the list which has not yet been evaluated. So initially we have
fibs = 0 : 1 : ...
And to inspect the ... (to see if it is [] or something that looks like n : ...), we evaluate some of the call to zipWith
Now consider what zipWith will do:
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
= 0 : 1 : zipWith (+) (0 : 1 : ...) (1 : ...)
= 0 : 1 : (0+1) : zipWith (+) (1 : (0+1) : ...) ((0+1) : ...)
But note, of course, that these evaluation steps only happen as one iterates through the elements of fibs (e.g. evaluating take 5 fibs will evaluate the first steps)

How to amend data in a box? ( J programming)

I have a complex box,
a =: 1 2 3 ; <4 ; < 5 6; <7 8
┌─────┬─────────────┐
│1 2 3│┌─┬─────────┐│
│ ││4│┌───┬───┐││
│ ││ ││5 6│7 8│││
│ ││ │└───┴───┘││
│ │└─┴─────────┘│
└─────┴─────────────┘
Suppose I know the address of inner box [5 6] is ( 1 1 0 ), i.e. the data can be extracted like this:
>0{>1{>1{a
5 6
My question is, how to write a function (verb) to amend the data given the address?
e.g. the address ( 1 1 0 ) is known, I want to change the value (5 6) into a small box (<123), the output should be:
┌─────┬───────────────┐
│1 2 3│┌─┬───────────┐│
│ ││4│┌─────┬───┐││
│ ││ ││┌───┐│7 8│││
│ ││ │││123││ │││
│ ││ ││└───┘│ │││
│ ││ │└─────┴───┘││
│ │└─┴───────────┘│
└─────┴───────────────┘
It may be achieved by a recursive function, but I'm wondering if the address can be applied directly, just like the way >0{>1{>1{a.
Thanks for the help!
You can determine the address (1;1;0) of the item that you are looking to replace/amend using the monadic primitive map ({::):
a=: 1 2 3;<4;<5 6;7 8
{:: a
┌───┬─────────────────────────┐
│┌─┐│┌─────┬─────────────────┐│
││0│││┌─┬─┐│┌───────┬───────┐││
│└─┘│││1│0│││┌─┬─┬─┐│┌─┬─┬─┐│││
│ ││└─┴─┘│││1│1│0│││1│1│1││││
│ ││ ││└─┴─┴─┘│└─┴─┴─┘│││
│ ││ │└───────┴───────┘││
│ │└─────┴─────────────────┘│
└───┴─────────────────────────┘
The dyadic form of {::, fetch, will return the item from the nested structure.
(1;1;0) {:: a
5 6
Unfortunately there is not currently an }:: equivalent to amend (}) although there is a recent request to implement the primitive }:: in the interpreter that has the functionality you desire.
While we wait for that primitive to be implemented, a search back through the J Programming forum archive revealed a post that suggested the following recursive adverb that does what you have asked for:
store=: adverb define
:
if. #m do. (< x (}.u)store ({.u){::y) ({.m)} y else. x end.
)
(<123) (1;1;0) store a
┌─────┬───────────────┐
│1 2 3│┌─┬───────────┐│
│ ││4│┌─────┬───┐││
│ ││ ││┌───┐│7 8│││
│ ││ │││123││ │││
│ ││ ││└───┘│ │││
│ ││ │└─────┴───┘││
│ │└─┴───────────┘│
└─────┴───────────────┘
I see your question has been also been asked and answered on the J Forum. In the interests of completeness I've added a link to that more general solution.
(<123) [ applyintree (1;1;0) a
┌─────┬───────────────┐
│1 2 3│┌─┬───────────┐│
│ ││4│┌─────┬───┐││
│ ││ ││┌───┐│7 8│││
│ ││ │││123││ │││
│ ││ ││└───┘│ │││
│ ││ │└─────┴───┘││
│ │└─┴───────────┘│
└─────┴───────────────┘

Combining results of different shapes without boxing/unboxing

Consider the following, which computes the successive cross products 0 cross 0 (1 result), then 0 1 cross 0 1 (4 results), then 0 1 2 cross 0 1 2 (9 results):
f=. (<#,"0/~&i.)"0
f 1+i.3
which outputs:
┌───┬───┬───┐
│0 0│ │ │
├───┼───┼───┤
│ │ │ │
├───┼───┼───┤
│ │ │ │
└───┴───┴───┘
┌───┬───┬───┐
│0 0│0 1│ │
├───┼───┼───┤
│1 0│1 1│ │
├───┼───┼───┤
│ │ │ │
└───┴───┴───┘
┌───┬───┬───┐
│0 0│0 1│0 2│
├───┼───┼───┤
│1 0│1 1│1 2│
├───┼───┼───┤
│2 0│2 1│2 2│
└───┴───┴───┘
Sometimes what we really want is the combined result, that is:
f=. [: (#~~:&a:) [: , (<#,"0/~&i.)"0
f 1+i.3
which outputs:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│0 0│0 0│0 1│1 0│1 1│0 0│0 1│0 2│1 0│1 1│1 2│2 0│2 1│2 2│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
The fundamental problem is that because each number outputs a different number of results (1, 4, 9, etc), we must arrive at our answer via the circuitous route of getting results with fill, only to immediately flatten and filter away that same fill.
Can we solve this with a reduction?
Another approach would be to think of this process as a reduction, which seems promising (no fill / filter) when we try it with 2 arguments:
(,/&([: ,/ ,"0/~&i.))/ 1 2
which outputs:
0 0 NB. <- result of left arg
0 0 NB. \
0 1 NB. \ these 4 result of right arg
1 0 NB. /
1 1 NB. /
However, if we try to reduce with a list of 3 or more elements, we'll get an error because after the first rightmost evaluation, the right argument will no longer be an atom, but the table result (ie, the result above) of the verb applied to the first 2 elements.
We can attempt to avoid this by applying our transformation only to one argument, say the left argument, but then the very last argument won't be processed:
(([: ,/ ,"0/~&i.)#[ , ])/ 1 2 3
which outputs:
0 0
0 0
0 1
1 0
1 1
3 3 NB. almost, but woops: the rightmost argument was not expanded
The question is: Is there a solution to the problem of combining results of different shapes in a single calculation, without filling and filtering?
I'm hoping there's a clever solution, but the straightforward solution that I would reach for is boxing the differently-shaped results.
<#f 1+i.3
┌─────┬─────────┬─────────────┐
│┌───┐│┌───┬───┐│┌───┬───┬───┐│
││0 0│││0 0│0 1│││0 0│0 1│0 2││
│└───┘│├───┼───┤│├───┼───┼───┤│
│ ││1 0│1 1│││1 0│1 1│1 2││
│ │└───┴───┘│├───┼───┼───┤│
│ │ ││2 0│2 1│2 2││
│ │ │└───┴───┴───┘│
└─────┴─────────┴─────────────┘
Then straighten out the contents of the boxes and unbox.
; , &. > <#f 1+i.3
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│0 0│0 0│0 1│1 0│1 1│0 0│0 1│0 2│1 0│1 1│1 2│2 0│2 1│2 2│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
And we can reduce the statement slightly by reordering.
; <#,#f 1+i.3
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│0 0│0 0│0 1│1 0│1 1│0 0│0 1│0 2│1 0│1 1│1 2│2 0│2 1│2 2│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

Resources