16 Jul 2013, 02:25 Dave Thomas (338 posts) (Harder) Write a function that takes a single-quoted string of the form number [+-*/] number and returns the result of the calculation. The individual numbers do not have leading plus or minus signs. `calculate('123 + 27') # => 150` A Possible Solution ```defmodule Parse do def calculate(expression) do { n1, rest } = parse_number(expression) rest = skip_spaces(rest) { op, rest } = parse_operator(rest) rest = skip_spaces(rest) { n2, [] } = parse_number(rest) op.(n1, n2) end defp parse_number(expression), do: _parse_number({ 0, expression }) defp _parse_number({value, [ digit | rest ] }) when digit in ?0..?9 do _parse_number({ value*10 + digit - ?0, rest}) end defp _parse_number(result), do: result defp skip_spaces([ ? | rest ]), do: skip_spaces(rest) defp skip_spaces(rest), do: rest defp parse_operator([ ?+ | rest ]), do: { &1+&2, rest } defp parse_operator([ ?- | rest ]), do: { &1-&2, rest } defp parse_operator([ ?* | rest ]), do: { &1*&2, rest } defp parse_operator([ ?/ | rest ]), do: { div(&1, &2), rest } end IO.inspect Parse.calculate('23+45') #=> 68 IO.inspect Parse.calculate('34 - 56') #=> -22 IO.inspect Parse.calculate('12 * 23') #=> 276 IO.inspect Parse.calculate('123 / 8') #=> 15 ``` 06 Sep 2013, 00:57 Arvind Dhiman (2 posts) I ended up browsing elixir library to find how to trim spaces in a string and convert string to a number. I have following implementation. I have regular expression twice in the function. How can I compile it so that I can reuse expression? ``````defmodule MyString do def eval(expression) do list = Regex.split(%r/\*|\+|\/|-/,expression) op = Regex.run(%r/\*|\+|\/|-/,expression) [n1,n2] = list _eval([_to_int(n1),_to_int(n2)], List.flatten(op)) end defp _to_int(n), do: binary_to_integer(String.strip(String.from_char_list!(n))) defp _eval([n1, n2], '+'), do: n1 + n2 defp _eval([n1, n2], '-'), do: n1 - n2 defp _eval([n1, n2], '/'), do: div(n1, n2) defp _eval([n1, n2], '*'), do: n1 * n2 end `````` 07 Sep 2013, 11:03 Daniel Rose (1 post) Here is mine. It basically uses `Enum.chunk_by/2` to extract the operands and the operator, and then uses pattern matching to do the correct calculation. ``````defmodule Calculator do def calculate(operation) do { operand1, operator, operand2 } = extract_parts(operation) calculate(operator, operand1, operand2) end defp calculate('+', operand1, operand2), do: operand1 + operand2 defp calculate('-', operand1, operand2), do: operand1 - operand2 defp calculate('*', operand1, operand2), do: operand1 * operand2 defp calculate('/', operand1, operand2), do: div(operand1, operand2) defp extract_parts(operation) do space? = &( &1 == (? ) ) operator? = &( &1 in '+-/*' ) [operand1, operator, operand2] = operation |> Enum.reject(space?) |> Enum.chunks_by(operator?) { chars_to_integer(operand1), operator, chars_to_integer(operand2) } end defp chars_to_integer(characters) do characters |> to_string |> binary_to_integer end end `````` 30 Oct 2013, 21:50 Roger Turner (7 posts) `````` def calculate(expression) do { left, rest } = Enum.split_while(expression, &(!(&1 in '+-*/'))) [ op | right ] = rest { result, _ } = Code.eval_quoted { list_to_atom([op]), [], [value(left), value(right)] } result end defp value(padded_digits), do: padded_digits |> to_string |> String.strip |> binary_to_integer `````` 04 Jan 2014, 01:54 Eric Liaw (3 posts) Since this chapter was shortly have list recursion I decided to try using Enum.reduce for extracting. Not really happy with how verbose _extract_helper ended up though… `````` def calculate(string_list) do {num1, op, num2} = _extract_components(string_list) case op do ?+ -> number(num1) + number(num2) ?/ -> number(num1) / number(num2) ?* -> number(num1) * number(num2) ?- -> number(num1) - number(num2) end end defp _extract_components(string_list) do Enum.reduce(string_list, {nil, nil, nil}, fn(elem, acc) -> _extract_helper(elem, acc) end) end # Ignore spaces defp _extract_helper(? , acc), do: acc defp _extract_helper(elem, {nil, nil, _num2}) when elem in '01234566789' do {[elem], nil, nil} end defp _extract_helper(elem, {num1, nil, _num2}) when elem in '01234566789' do {num1 ++ [elem], nil, nil} end defp _extract_helper(elem, {num1, nil, _num2}) when elem in '/+-*' do {num1, elem, nil} end defp _extract_helper(elem, {num1, op, nil}) when elem in '01234566789' do {num1, op, [elem]} end defp _extract_helper(elem, {num1, op, num2}) when elem in '01234566789' do {num1, op, num2 ++ [elem]} end defp number(characters) do characters |> to_string |> binary_to_integer end `````` 12 Jan 2014, 04:40 Daniel Ashton (7 posts) This would have been four lines shorter if I had found a way to dynamically reference Kernel.+/2 and friends. `````` def calculate(str) do {x, op, y} = _parse(str, {0, :op, 0}) op.(x, y) end defp _parse([] , acc ) , do: acc defp _parse([h | t], {a, b, c}) when h in ?0..?9, do: _parse(t, {a, b, c * 10 + h - ?0}) defp _parse([h | t], {_, _, c}) when h in '+-*/', do: _parse(t, {c, _fn(h), 0}) defp _parse([_ | t], acc ) , do: _parse(t, acc) defp _fn(?+), do: &Kernel.+/2 defp _fn(?-), do: &Kernel.-/2 defp _fn(?*), do: &Kernel.*/2 # defp _fn(?/), do: &Kernel.//2 # Nope, guess again defp _fn(?/), do: &div/2 # or &(&1 / &2) or ("#{div &1, &2} remainder #{rem &1, &2}") `````` 14 Jan 2014, 01:22 Daniel Ashton (7 posts) Thanks to José Valim answering my question on StackOverflow, here’s a version that calls the operator dynamically: `````` def calculate(str) do {x, op, y} = _parse(str, {0, :op, 0}) apply :erlang, list_to_atom(op), [x, y] end defp _parse([] , acc ) , do: acc defp _parse([h | t], {a, b, c}) when h in ?0..?9, do: _parse(t, {a, b, c * 10 + h - ?0}) defp _parse([h | t], {_, _, c}) when h in '+-*/', do: _parse(t, {c, [h], 0}) defp _parse([_ | t], acc ) , do: _parse(t, acc) `````` 24 Jan 2014, 13:16 Dmitriy Ukhalov (1 post) ``````defmodule Parse do def calculate(str), do: _calculate(str, 0) defp _calculate([], value), do: value defp _calculate([? | tail], value), do: _calculate(tail, value) defp _calculate([digit | tail], value) when digit in '0123456789', do: _calculate(tail, value * 10 + digit - ?0) defp _calculate([operator | tail], value) when operator in '+-*/', do: apply(:erlang, list_to_atom([operator]), [value, calculate(tail)]) end IO.inspect Parse.calculate('23+45') #=> 68 IO.inspect Parse.calculate('34 - 56') #=> -22 IO.inspect Parse.calculate('12 * 23') #=> 276 IO.inspect Parse.calculate('123 / 8') #=> 15.375 # Also: IO.inspect Parse.calculate('123 + 40 / 8') #=> 128.0 IO.inspect Parse.calculate('123 * 10 / 2') #=> 615.0 `````` You must be logged in to comment