# Convert ruby while loop into elixir

Hi to all
I’m new to elixir and I’m learning it. I have a question maybe noob question but I did not find a solution.

``````p = [1.01, 1.02, 1.04]
i= 1.01
while p.any? {|x| i == x } do
i += 0.01
puts i
end

sh-4.3\$ ruby main.rb
1.02
1.03
``````

Probably the most literal translation would be:

``````defmodule Looper do
def loop(p, i) do
case i in p do
true -> loop(p, i + 0.01)
false -> i
end
end
end

p = [1.01, 1.02, 1.04]
i = 1.01

Looper.loop(p, i)
``````

There’s likely other ways to do it. Recursion generally solves most scenarios where you’d use a `while` loop.

2 Likes

The obvious ‘other way’ to do it would be using streams:

``````defmodule Looper do
def loop(p, i) do
i
|> Stream.iterate(fn x -> x + 0.01 end)
|> Stream.take_while(&Kernel.in(&1, p))
|> Enum.to_list
|> List.last
end
end
``````

but in this simple case it would generate a lot more overhead than the plain recursive version, thus being slower.

1 Like

It does not work as expected:

``````Looper.loop(p,  i)
1.02``````

Add a `IO.puts i` before the `loop` call in the case expression so that you get the following:

``````true ->
IO.puts i
loop p, i + 0.01
``````

I would use a Map.reduce to solve this.

``````p = [1.01, 1.02, 1.04]
i = 1.01

Enum.reduce(p, i, fn
x, acc when x == acc ->
acc = acc + 0.01
IO.puts acc
acc
_, acc -> acc + 0.01
end)
``````

If your not familiar with multi clause anonymous functions, here is another solution:

``````p = [1.01, 1.02, 1.04]
i = 1.01

Enum.reduce(p, i, fn x, acc ->
new_acc = acc + 0.01
if x == acc do
IO.puts new_acc
end
new_acc
end)
``````

Here is the output from iex

``````iex(20)> p = [1.01, 1.02, 1.04]
[1.01, 1.02, 1.04]
iex(21)> i = 1.01
1.01
iex(22)> Enum.reduce(p, i, fn
...(22)>   x, acc when x == acc ->
...(22)>     acc = acc + 0.01
...(22)>     IO.puts acc
...(22)>     acc
...(22)>   _, acc -> acc + 0.01
...(22)> end) ; nil
1.02
1.03
nil
iex(23)>
``````

Note the `; nil` at the end is just to stop the result of the reduce from printing in iex…

This isn’t equivalent to the original though. The original iterated over the value not the list.

I think in this case it is definitely better to follow the KISS principle and go for the straightforward recursive loop which is way simpler than the other alternatives. 2 Likes

I’ve been playing with a while macro here https://hex.pm/packages/while , it can make the code look very very similar to the ruby version:

``````    import While

p = [1.01, 1.02, 1.04]
i = 1.01
while_with i, Enum.any?(p, &(i == &1)) do
IO.puts i
i + 0.01
end
``````

This while macro can be installed by adding while to your list of dependencies in mix.exs:

``````def deps do
[
{:while, "~> 0.1.0"}
]
end
``````

I think it’s interesting that that can even work, I’m curious what the macro is doing. Definitely not a recommended solution though. Functional code is a lot more idiomatic and clear.

EDIT:

Ah in looking at the examples I see that if you want `i` to carry on with the value it has to be rebound, that makes more sense:

``````  cnt = while cnt, cnt < 10 do
cnt + 1
end
``````
1 Like