6
$\begingroup$

I am trying to improve some pieces of coding I use frequently and am stuck with the following. I suspect there is a very similar answer but was unable to find it.

Function fun with 3 arguments "walks" over three lists of equal length (contained in sp). The 4th argument a is the value of the function in the last step. Essentially, I want to MapThread at position I across the length of the lists while also using the last evaluation. I tried to use Fold or Nest together with Reap/Sow but to no avail. The output is the evaluation of the function, and a working example with a Do loop is as follows.

sp = RandomInteger[{1, 10}, {3, 10}]
fun[{x_, y_, z_, a_}] := 2 x - y + 3 z - 2 a
atposI = 0; lastval := 0; output := {};

Do[{
atposI = Append[sp[[All, i]], lastval];
lastval = fun[atposI];
output = Append[output, lastval]
}, {i, 1, 10}]
output

Input:

{{7, 1, 5, 10, 10, 2, 5, 10, 10, 6}, {4, 1, 4, 2, 5, 2, 2, 4, 8, 10}, {2, 6, 1, 4, 6, 7, 4, 1, 2, 8}}

Result

{16, -13, 35, -40, 113, -203, 426, -833, 1684, -3342}

I am particularly interested in solutions that contain Fold, Nest and Reap/Sow, although any answer is more than welcome.

$\endgroup$
1
  • $\begingroup$ FoldPairList does this, or similar enough, if I understand your question right. $\endgroup$
    – Roman
    May 5 '20 at 17:41
3
$\begingroup$

An alternative to FoldList could be FoldPairList:

FoldPairList[
  Function[{prev, new}, {#, #}&@ ({2, -1, 3}.new - 2 prev) ],
  0,
  Transpose@sp
]

(* {16, -13, 35, -40, 113, -203, 426, -833, 1684, -3342} *)

In response to the request of keeping function fun definition, you can modify the above to obtain the same result as follows:

ClearAll[fun]
fun[{x_, y_, z_, a_}] := 2 x - y + 3 z - 2 a

FoldPairList[
  Function[{prev, new}, {#, #}&@ fun[{Sequence @@ new, prev}]],
  0,
  Transpose@sp
]
$\endgroup$
2
  • $\begingroup$ Many thanks for your answer. As I commented to another post, is it possible to retain function fun "as is" in the code and not rely on it being linear and use the coefficients? $\endgroup$
    – Titus
    May 5 '20 at 19:00
  • 1
    $\begingroup$ @Titus Sure, although it may be a bit awkward-looking. See edit. $\endgroup$
    – MarcoB
    May 5 '20 at 19:15
9
$\begingroup$

Indeed, FoldList[] would be the appropriate operation here:

FoldList[{2, -1, 3, -2}.Append[#2, #1] &, 0, 
         Transpose[{{7, 1, 5, 10, 10, 2, 5, 10, 10, 6},
                    {4, 1, 4, 2, 5, 2, 2, 4, 8, 10},
                    {2, 6, 1, 4, 6, 7, 4, 1, 2, 8}}]] // Rest
   {16, -13, 35, -40, 113, -203, 426, -833, 1684, -3342}

Of course, modifying the snippet above to use fun[] in the OP is not too hard to do (spot the difference!):

FoldList[fun[Append[#2, #1]] &, 0, 
         Transpose[{{7, 1, 5, 10, 10, 2, 5, 10, 10, 6},
                    {4, 1, 4, 2, 5, 2, 2, 4, 8, 10},
                    {2, 6, 1, 4, 6, 7, 4, 1, 2, 8}}]] // Rest
$\endgroup$
2
  • $\begingroup$ Many thanks for an elegant answer. I would just like to note that my function is a toy function which will be replaced by something complicated. How would your answer change if fun had to be used "as is", i.e. without using the {2, -1, 3, -2}.Append[#2, #1] & structure? $\endgroup$
    – Titus
    May 5 '20 at 18:59
  • 1
    $\begingroup$ See the edited version. $\endgroup$
    – J. M.'s torpor
    May 6 '20 at 0:22
3
$\begingroup$
foldList = Rest @* FoldList[fun @* Flatten @* Reverse @* List] @* Prepend[0] @* Transpose;

Example:

sp = {{7, 1, 5, 10, 10, 2, 5, 10, 10, 6}, {4, 1, 4, 2, 5, 2, 2, 4, 8, 10}, 
  {2, 6, 1, 4, 6, 7, 4, 1, 2, 8}};

foldList @ sp
{16, -13, 35, -40, 113, -203, 426, -833, 1684, -3342}

Also

foldList2 = Transpose /* Prepend[0] /* FoldList[fun @* Flatten @* Reverse @* List] /* Rest

foldList2 @ sp
 {16, -13, 35, -40, 113, -203, 426, -833, 1684, -3342}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.