Is it possible to update multiple values in Enum.reduce without using maps as accumulator?

So, from what I know, we can only update one value using Enum.reduce/3. Is it possible to update multiple values using Enum.reduce/3 without using maps?

To generalize this in more coding terms, I want to update the values of both mycurr and myglobal. Now if we use Enum.reduce/3, we cannot update both of these without incorporating them in a map and passing that off as an accumulator. The reason why I don’t want to use maps here is because that would increase the space complexity to O(n).

An example of what I want to do in Python is like this:

  mylist = [1,2,3,4,5]
  mycurr = mylist[0]
  myglobal = mylist[0]
  for val in mylist:
    if mycurr < 0:
      mycurr = val
    else:
      mycurr += val

    if myglobal < mycurr:
       myglobal = mycurr

Over here, both mycurr and myglobal gets updated. In Elixir, if we try using Enum.reduce/3, we can only update one of this unless we use a map as an accumulator, which would increase the space complexity to O(n).

Enum.reduce(mylist, 0, fn(val, acc) -> # 0 here refers to acc which is one one of mycurr or myglobal

Are there any better alternatives to using Enum.reduce/3 for this or is it possible to update multiple values without using a map as an accumulator?

The accumulator can be anything you want it to be, and for your example a tuple would suffice which is both space and time efficient. Something like:

{my_curr, my_global} =
  Enum.reduce(mylist, {my_curr, my_global}, fn 
    val, {curr, global} when curr < 0 -> {val, global}
    val, {curr, global} when global < curr -> {curr, curr}
    ...
  end)

Of course the function clauses aren’t what you’re after exactly since the code is truncated, but hopefully that gives you and idea of one approach.

4 Likes

This is brilliant. Thank you very much!

While kip is correct in that tuples are more efficient on paper I’d also add that O(n) for n = 2 is also not really inefficient – problems arise for large values for n. Adding to that the implementation detail that maps with less than 32 keys are stored as tuples behind the scenes the difference in efficiency is likely almost none.

2 Likes

Good to know. :+1:

My understanding is that access to tuple elements is O(1) (ie constant time). See also the erlangist.com section on tuples

Accessing any element takes constant time

Modifying it is O(n) (ie linear time), meaning exactly as @LostKobrakai says. It might be more efficient to use a 2-element list but I suspect the timing wouldn’t be much different.

1 Like