#Loop with accumulator in Elixir

49 messages · Page 1 of 1 (latest)

mild inlet
#

How can I rewrite following loop from JS to elixir?

let sum = 0
let results = []

for i in [1,2,3,4,5]{
  { num, res } = some_fun(sum)
  results.push(res)
  sum = sum + num
}
  
burnt lance
naive sluice
#

you'll need to use Enum.reduce to accumulate both the sum and the results list

#

(or the reduce option of for)

mild inlet
#

I'm not sure how syntax would look like for both sum and res

#

I'm a newbie

#

I know how to do Enum.reduce over one variable

burnt lance
#

You can use a tuple that contains both variables

elfin hazel
mild inlet
#

Thanks guys, I should have looked at documentation more carefully

#

Didn't know i can return tuple {result, accumulator }

#

Btw. is there a way to do the same thing with comprehensions?

sick salmon
#

Comprehensions are insanely powerful in elixir. Much more so than in, say, python

mild inlet
mild inlet
#

so I should do acc -> {result, accumulator } ?

sick salmon
#

You need to give the tuple to reduce:

#

acc is the accumulator, so you'll need to pattern match out the tuple there and return a tuple from the body of the comprehension

#

So, {result, sum} -> something

#

Where something applies the computation and returns a new tuple

mild inlet
#

Like this?

   initial = 10
    for idx <- 1..10, reduce: {initial, []} do
      {res, acc} -> some_fun(acc)
    end
    
    def some_fun(acc) do
      {%{hello: "world"}, acc+1}
    end
sick salmon
#

Well, acc in that instance is a list and you can't add to it with +

mild inlet
#

acc is number, res should be list. This doesn't work

#

Or I'm getting something wrong

sick salmon
#

No, it's not. It's an empty list initially

#

Idx is a number

#

You don't seem to be doing anything with it

mild inlet
#

Correction

   initial = 10
    for idx <- 1..10, reduce: {[], initial} do
      {res, acc} -> some_fun(acc, idx)
    end
    
    def some_fun(acc, idx) do
      {%{hello: "world"}, acc+idx}
    end
elfin hazel
#

You need to update some_func, but yes

#

It would work

#

No, wait

mild inlet
#

well it doesn't 😄

elfin hazel
#

It will not

sick salmon
#

Do you want to append a bunch of maps to the list?if you want to do that, you'll need to wither pass the list to the function and append in there, or take the return value of the function, pattern match out the parts and append the map to the list in the body of the for

elfin hazel
#

If you want to make it simple:

Enum.map_reduce(1..10, 0, &some_fun(&2, &1))

And it will do what you want

sick salmon
elfin hazel
#

They wanted to achieve what is in the question, it was latter sugested to use comprehesions, but first proposal was to use Enum.reduce/3 which was rejected only becasue @mild inlet didn't know how to do reduce over multiple values

sick salmon
#

And then the conversation moved on and they asked if it could be done in a comp

elfin hazel
#

Ahhh, I missed it

sick salmon
#

And it can, but it's not as concise

elfin hazel
#

Indeed

mild inlet
#

Alright, I did it with Enum.map_reduce. Thanks for the help guys

sick salmon
#

I'm at a keyboard... Here's how you'd do it in a comp:

initial = 10
    for idx <- 1..10, reduce: {[], initial} do
      {res, acc} -> {[%{hello: "world"} | res], acc + idx}
    end
elfin hazel
#

You will need to reverse the result list afterwards

sick salmon
#

not in this example, since they're all the same 😉