tbk
Reusing function variables in a `when` conditional
I have a function with a condition:
def iteraterefs(divisor, number, table, out, i) when number >= divisor do
number = number - divisor
out = out <> elem(table, i)
end
However it seems that the compiler does not like the condition when number >= divisor do, is there something about reuse of function variables I am missing from the documentation?
Marked As Solved
dimitarvp
IMO this thread is getting a bit too micro, you are kind of posting an error after error and I think you should step back and just post your entire module source code, and state the end goal.
It also sounds like you haven’t practiced Elixir enough if lack of mutability and the lexical scope are still surprising for you. Exercism requires some understanding of the language’s constructs. Without that you’ll just be crashing into one error after another, as it seems it is happening currently.
Also Liked
al2o3cr
FunctionClauseError is what I’d expect from iteraterefs if it was called with number < divisor. You likely need to define it for those inputs as well to pass Exercism’s tests.
General note: this isn’t going to do what you want. i = i + 1 rebinds the name i inside the do / end block but that value doesn’t escape or even make it to the next iteration.
Same thing for out = out <> elem(table, i) inside iteraterefs; the name out is bound to a new name but then the scope ends immediately.
My recommendation would be to forget completely about Enum.each for a little while; it is almost never the right solution in Elixir.
tbk
I went away, far up into the mountains which are the Elixir documents, I learned many things and when I returned to take on the challenge again, it neatly folded before me like the recursion I used to solve it:
defmodule RomanNumerals do
@doc """
Convert the number to a roman number.
"""
@table {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}
@refs {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
@spec numeral(pos_integer) :: String.t()
def numeral(number) do
iterate(number, @refs, @table, 0, "")
end
defp iterate(num, refs, table, index, out) do
if num == 0 do
out
else
ref = elem(refs, index)
cond do
num >= ref -> sym = elem(table, index)
iterate(num - ref, refs, table, index, out <> sym)
true -> iterate(num, refs, table, index + 1, out)
end
end
end
end
benwilson512
Nicely done, a big improvement! Probably the only other change I’d advocate for is to do this:
defp iterate(0, _refs, _table, _index, out) do
out
end
defp iterate(num, refs, table, index, out) do
ref = elem(refs, index)
cond do
num >= ref ->
sym = elem(table, index)
iterate(num - ref, refs, table, index, out <> sym)
true ->
iterate(num, refs, table, index + 1, out)
end
end
It’s conventional that when you’re doing recursion like this to define the “base case” or “termination case” as its own clause up front, and then you have other clauses after. This is entirely a stylistic thing though, so it’s up to you!








