Search code examples
j

How do I arbitrarily compose dyadic functions while bonding one side of their arguments in J?


I have a dyadic function, call it f, that consumes a number and an array and outputs another array. For example:

   0 f x_0
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

where x_0 =: 1+i.30.

I want to run this an arbitrary number of times, but feeding the array result of the computation into the next invocation of f. For example, for 3 (the output array will seem arbitrary, but it's correct):

   3 f 2 f 1 f 0 f x_0
2 8 6 9 4 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

I would like a function of the form x g y, where x is a number and y is the starting array, and it will apply g x number of times using y as the initial input.

I've attempted to use suffix \. for this, but I'm having trouble figuring out how to pass x_0 as the initial list to f. I don't just need to do 2 f 1 f 0 f. It seems like I need a function that takes a number, and then chains that many functions together while &ing the integer argument to f. This fails, though. For example:

   f2 =: 3 & f 2 & f 1 & f 0 & f
   f2 x_0
|index error: g1
|       f2 x_0

This seems like it's close, but I think I'm misunderstanding something about how & works (g1 is an internal function that I can share if necessary). I think I want something like: f(2, f(1, f(0, y))), and y is my initial list, but I don't want to have to explicitly call f.

I poked around some more and made more progress with composition:

   f2 =: (2 & f) @ (1 & f) @ (0 & f)
   f2 x_0
4 6 2 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

This works, but I'm a bit lost as to how to compose an arbitrary number of functions. How do I compose f an arbitrary number of times while also bonding its left argument to individual elements of an array of decreasing numbers? I feel like there's a more "intrinsic" J way to do this, but I haven't been able to find anything in the documentation so far.

Thank you, and let me know if you need more information.


Solution

  • Neat question and this is the way that I approached it, but as you work with J you will find there could be other ways.

    My solution is:

       g=. 4 : '> (f~&.>)/ (<y),(<"0@i.x)'
       f=. +   NB. + is placeholder for whatever f is
       a_0 =. 3
       b_0 =. 1 + i. 10
       a_0 g b_0
    4 5 6 7 8 9 10 11 12 13
    

    To break down the steps:

    (<b_0),(<"0 i.a_0) NB. append boxed integers of  i. a_0 to boxed b_0
    ┌────────────────────┬─┬─┬─┐
    │1 2 3 4 5 6 7 8 9 10  │0│1│2│
    └────────────────────┴─┴─┴─┘
       (f~&.>)/ (<b_0),(<"0 i.a_0) NB. arguments of f are reversed and unboxed, f operates and then boxes result and moves to next item
    ┌───────────────────────┐
    │4 5 6 7 8 9 10 11 12 13  │
    └───────────────────────┘
       > (f~&.>)/ (<b_0),(<"0 i.a_0) NB. result is unboxed
    4 5 6 7 8 9 10 11 12 13
    

    In tacit form g looks like this:

       gt=.  >@:(f~ &.>) / @:((<@]) , |.@:<"0@i.@[)
       a_0 gt b_0
    4 5 6 7 8 9 10 11 12 13