Michael Lubas, 2022-09-14
Write a function in Elixir named build_string
that takes 4 arguments:
a - An integer
b - An integer
ops - A list of the atoms [:add, :sub, :mul, :div] in any order, max length of 10
sep - A separator, such as "-" or ","
and returns a string where a and b are calculated, in-order, together according to the list of operations, delimited by the separator value. For example:
> build_string(8, 2, [:add, :sub], ",")
> "10,6"
> build_string(10, 5, [:div, :sub, :mul, :add], "-")
"2-5-50-15"
Here is an example of bad style:
defmodule Codex do
# Bad style
def build_string(a, b, ops, sep) do
Enum.reduce(ops, "", fn op, acc ->
acc <> sep <> Integer.to_string(calculate(a, b, op))
end)
|> String.trim_leading(sep)
end
def calculate(a, b, op) do
case op do
:add ->
a + b
:sub ->
a - b
:mul ->
a * b
:div ->
div(a, b)
end
end
end
Focus on the build_string/4
function:
def build_string(a, b, ops, sep) do
Enum.reduce(ops, "", fn op, acc ->
acc <> sep <> Integer.to_string(calculate(a, b, op))
end)
|> String.trim_leading(sep)
end
From the start at Enum.reduce
, the accumulator is a blank string, so reduce is creating a string. It is iterating over ops, so the accumulator will be built up in steps:
> build_string(8, 2, [:add, :sub], ",")
> # inside Enum.reduce
> "" <> "," <> "10"
> ",10"
> ",10" <> "," <> "6"
> ",10,6"
Then there is a call to trim the leading sep
, in this case ","
. The call to String.trim_leading/2
here is a red flag that there is a special case in the code evaluation, an additional leading separator value that should not have been introduced.
Now consider this version of build_string/4
:
# Good style
def build_string(a, b, ops, sep) do
ops
|> Enum.map(&calculate(a, b, &1))
|> Enum.join(sep)
end
Enum.map
conveys that calculate/3
transforms every element in the list, preserving the order. Elixir provides Enum.join
, but even if it was not included, writing a function to join all the elements of the list together with a separator value makes this much more clear.
> build_string(8, 2, [:add, :sub], ",")
> [10, 6] # Produced by Enum.map([:add, :sub], &calculate(8, 2, &1))
> "10,6" # Produced by Enum.join([10, 6], ",")
This avoids the problem of building up a string where the leading separator needs to be removed. The problem given may seem trivial, and it is. Making your code as easy to read as possible, by avoiding unnecessary cases, is the important point.
Paraxial.io stops data breaches by securing your Elixir and Phoenix apps. Detect and fix critical security issues today. Attending ElixirConf EU (April 17th) in Lisbon? Paraxial.io founder Michael Lubas is giving the training Elixir Application Security and will be speaking at the conference. Hope to see you there!
Subscribe to stay up to date on new posts.