Can someone please tell me what have I done wrong here ?
defmodule Calculus do
#calculate(expression) takes a single-quoted string of the form of number [+-*/] number and does the calculation
#ex : calculate('5*3') => 15
def calculate(expression) when (is_list(expression) and ?+ in expression) do
term1(expression) + term2(expression)
end
def calculate(expression) when (is_list(expression) and ?- in expression) do
term1(expression) - term2(expression)
end
def calculate(expression) when (is_list(expression) and ?* in expression) do
term1(expression) * term2(expression)
end
def calculate(expression) when (is_list(expression) and ?/ in expression) do
div(term1(expression), term2(expression))
end
# term1(expression) returns the first term and transforms it to integer
# ex : Calculate.term1('123+124') = '123'
defp term1(expression) when is_list(expression) do
List.to_integer(elem(split_terms(expression),0))
end
# sign(expression) returns the sign
# ex : Calculate.sign('123+124') = '+'
defp sign(expression) when is_list(expression) do
[hd(elem(split_terms(expression),1))]
end
# term2(expression) returns the second term transforms it to integer
# ex : Calculate.term1('123+124') = '124'
defp term2(expression) when is_list(expression) do
List.to_integer(tl(elem(split_terms(expression),1)))
end
# split-terms(expression) splits the expression to term1 and sign++term2
# ex : Calculate.split_terms('123+124') = {'123', '+124'}
defp split_terms(expression) do
Enum.split_while(expression, &(&1 in ?0..?9))
end
end
The full error message explains it:
** (ArgumentError) invalid args for operator “in”, it expects a compile-time list or compile-time range on the right side when used in guard expressions, got: expression
i.e. you are trying to use it on a runtime list which could take a long time to run Enum.member?/2 on.
To understand the constraints of guard sequences please see Erlang Reference Manual; Expressions: 8.25 Guard Sequences
Essentially only operations that have no side effects and are predictably “fast” are allowed in guards.
You are probably better off recursively scanning through expression
producing an operand
, operator
and rest
any time you encounter one of “±*/”.
2 Likes
Thank you, after reading the documentation I understood what expression can be used as guard.
I learned Regex and applied it in this example.
My program now works even when the argument contains spaces.
defmodule Calculus do
def calculus(expression) when is_binary(expression) do
expression |> _split_terms |> _terms_to_integer |> _operation
end
# operation executes the calculation depending on the operator
defp _operation([left,"+",right]) do
left + right
end
defp _operation([left,"*",right]) do
left * right
end
defp _operation([left,"-",right]) do
left - right
end
defp _operation([left,"/",right]) do
left / right
end
# Transforms the terms to integers
#ex : Calculus._terms_to_integer([" 128 + 50 ", "128", "+", "50"]) = [128, "+", 50]
defp _terms_to_integer([_,left,operator,right]) do
[String.to_integer(left),operator, String.to_integer(right)]
end
# Separate terms
# ex : Calculus._split_terms(" 128 + 50 ") = [" 128 + 50 ", "128", "+", "50"]
defp _split_terms(expression) do
Regex.run(~r/\s*(\d+)\s*([+-\/*])\s*(\d+)\s*/,expression)
end
end