# Function gives "2nd argument: not valid character data" error

I am new to elixir and I am trying to make a recursive anonymous function, but for some reason my anynymous function that works on it’s own as expected, throws me “2nd argument: not valid character data (an iodata term)” error.

Here is code:

``````calcTip = fn bill ->
if bill >= 50 and bill <= 300, do: bill * 0.15, else: bill * 0.2
end

bills = [ 22, 295, 176, 440, 37, 105, 10, 1100, 86, 52 ]

calcTipsAndTotals = fn index, tips, totals, recursiveFn ->
case index < length(bills) do
true ->
new_tip = calcTip.(Enum.at(bills, index, 0))
new_tips = tips ++ [new_tip]
new_totals = totals ++ [Enum.at(new_tips, index) + Enum.at(bills, index)]
recursiveFn.(index + 1, new_tips, new_totals, recursiveFn)
false -> [tips, totals]
end
end

IO.puts(
calcTipsAndTotals.(0, [], [], calcTipsAndTotals)
)

``````
1 Like

Not directly related to your question, but a general tip for new Elixir devs: calling functions like `length` and `Enum.at` inside a loop should make you slightly worried about performance.

The reason is that both of those functions take time that’s proportional to the size of the input (`bills` here usually), unlike other languages where arrays can be accessed in a constant amount of time. This means that calculating something like `length(bills)` inside a recursion over `bills` will immediately be accidentally quadratic.

IMO a good general principle is to avoid using indexes as much as possible. For instance, in your code above, every call to `Enum.at` uses the same index so the whole thing can be rewritten as an `Enum.map`:

``````calcTip = fn bill ->
if bill >= 50 and bill <= 300, do: bill * 0.15, else: bill * 0.2
end

bills = [ 22, 295, 176, 440, 37, 105, 10, 1100, 86, 52 ]

tips_and_totals_as_pairs =
Enum.map(bills, fn bill ->
tip = calcTip.(bill)
{tip, tip + bill}
end)

tips_and_totals = Enum.unzip(tips_and_totals_as_pairs)
``````

or an alternate version with explicit recursion, if that’s a requirement:

``````calcTip = fn bill ->
if bill >= 50 and bill <= 300, do: bill * 0.15, else: bill * 0.2
end

bills = [ 22, 295, 176, 440, 37, 105, 10, 1100, 86, 52 ]

calc_tips_and_totals = fn
[bill | rest], tips, totals, recursive_fn ->
tip = calcTip.(bill)
calc_tips_and_totals(rest, [tip | tips], [tip + bill | totals], recursive_fn)
[], tips, totals, _ ->
[Enum.reverse(tips), Enum.reverse(totals)]
end

tips_and_totals = calc_tups_and_totals(bills, [], [], calc_tips_and_totals)
``````

Some general notes from the above:

• to know when to stop, instead of checking `length` (which is expensive) this pattern-matches on `[bill | rest]` vs `[]` (which is super-cheap)
• instead of appending to the end of lists with `totals ++ [new_value]`, this adds to the beginning of the list (super-cheap again) and then reverses at the end. See the BEAM Efficiency Guide for some additional discussion on this.
• both of the versions above produce separate lists for `tips` and `totals`, but you may want to consider keeping those things together either as a tuple (omit the `Enum.unzip`) or even a map/struct. That way the values for a particular bill are always in one place, instead of spread across multiple lists.
4 Likes