small medium large xlarge

Dave_gnome_head_isolated_pragsmall
16 Jul 2013, 02:25
Dave Thomas (387 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</summary>

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

</details>

Generic-user-small
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 

Generic-user-small
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
Mac 128k logic board 96x96_pragsmall
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

Generic-user-small
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
Generic-user-small
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}")
Generic-user-small
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)
Generic-user-small
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
Generic-user-small
12 Nov 2014, 03:31
Elliot Finley (13 posts)

I have to admit I lost my patience on this one and just cheated :)

defmodule Calc do
  def eval(expression) do
    {result, _} = Code.eval_string(expression)
    result
  end
end
Maik_schmidt_avatar2_pragsmall
28 Mar 2015, 10:41
Maik Schmidt (120 posts)

Dave’s original solution no longer works with recent versions of Elixir. You’ll get the following output:

pi@elixirpi ~/learn_elixir $ elixir parse.exs
parse.exs:19: warning: found ? followed by codepoint 0x20 (space), please use \s instead
** (CompileError) parse.exs:22: unhandled &1 outside of a capture
    (stdlib) lists.erl:1352: :lists.mapfoldl/3
    (stdlib) lists.erl:1352: :lists.mapfoldl/3
    (elixir) src/elixir_clauses.erl:36: :elixir_clauses.clause/7

Here’s a version that works with the latest version of Elixir:

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([ ?\s | 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
Generic-user-small
05 May 2015, 14:48
Derick Thomas (3 posts)

This is my solution. Turned out to be much simpler than initially what I thought.


defmodule Parse do
  defp _calculate([], num1, ?+, num2) do: num1 + num2
  defp _calculate([], num1, ?-, num2) do: num1 - num2
  defp _calculate([], num1, ?*, num2) do: num1 * num2
  defp _calculate([], num1, ?/, num2) do: num1 / num2
  defp _calculate([head | tail], num1, ?=, 0) when head == 32, do: _calculate(tail, num1, ?=, 0)
  defp _calculate([head | tail], num1, ?=, 0) when head in [?+, ?-, ?*, ?/], do: _calculate(tail, num1, head, 0)
  defp _calculate([head | tail], num1, ?=, 0), do: _calculate(tail, (num1*10)+(head-?0), ?=, 0)
  defp _calculate([head | tail], num1, op, 0) when head == 32, do: _calculate(tail, num1, op, 0)
  defp _calculate([head | tail], num1, op, num2), do: _calculate(tail, num1, op, (num2*10)+(head-?0))

  def calculate(string) do
    _calculate(string, 0, ?=, 0)
  end
end

iex(90)> Parse.calculate('123000 + 27')            
123027
iex(91)> Parse.calculate('123000+27')  
123027
iex(92)> Parse.calculate('123000 +27')
123027
iex(93)> Parse.calculate('123000+ 27')



Generic-user-small
10 May 2015, 17:36
T. Kort (1 post)

Conveniently with strings:

defmodule Comp do

  def calc(charl) do
    [op1, op, op2] = to_string(charl) |> String.split
    ops = [op1, op2] |> Enum.map(&String.to_integer/1)
    fun = fn
      "+", [op1, op2] -> op1 + op2
      "-", [op1, op2] -> op1 - op2
      "*", [op1, op2] -> op1 * op2
      "/", [op1, op2] -> op1 / op2
    end
    fun.(op, ops)
  end
end
Dj-mangatar_pragsmall
21 May 2015, 21:01
Wirianto Djunaidi (5 posts)

I tried to solve this without using built in functions, I reused Dave’s example in the book for converting digit to integer.

defmodule MyString do

  def calculate(str) do
    {left, op, right} = _parse(str, '')
    _calculate(_number_digits(left, 0), _number_digits(right, 0), op)
  end

  defp _calculate(left, right, op) when op == ?+, do: left + right
  defp _calculate(left, right, op) when op == ?-, do: left - right
  defp _calculate(left, right, op) when op == ?*, do: left * right
  defp _calculate(left, right, op) when op == ?/, do: left / right

  defp _parse([], value), do: { value, '', ' ' }
  defp _parse([head | tail], value) when head in '+-*/' do
    { value, head, tail } 
  end
  defp _parse([head | tail], value) do
    _parse(tail, value ++ [head])
  end

  defp _number_digits([], value), do: value
  defp _number_digits([ digit | tail ], value)
  when digit in '0123456789' do
    _number_digits(tail, value * 10 + digit - ?0)
  end
  defp _number_digits([ digit | tail ], value)
  when digit in ' _' do
    _number_digits(tail, value)
  end

end
2011-11-23-square_pragsmall
12 Jun 2015, 08:35
Daniel Garcia (4 posts)

My solution is like T. Kort’s but a little shorter. With the problem it won’t work if the numbers are not separated by spaces.

defmodule MyString
  def parse(xs) do
    [num1, operator, num2] = to_string(xs) |> String.split

    apply Kernel, String.to_atom(operator), Enum.map([num1, num2], &String.to_integer/1)
  end
end
Generic-user-small
19 Jun 2015, 17:06
Sasa Ranisavljevic (1 post)
defmodule Strings do
  def calculate(exp), do: _calculate(exp, 0)
  defp _calculate([], value), do: value
  defp _calculate([? | t], value), do: _calculate(t, value)
  defp _calculate([?+ | t], value), do: value + _calculate(t, 0)
  defp _calculate([?- | t], value), do: value - _calculate(t, 0)
  defp _calculate([?* | t], value), do: value * _calculate(t, 0)
  defp _calculate([?/ | t], value), do: value / _calculate(t, 0)
  defp _calculate([digit | t], value) when digit in '0123456789' do
    _calculate(t, value*10 + digit - ?0)
  end
end

There is, however, this error:

warning: found ? followed by codepoint 0x20 (space), please use \s instead

Generic-user-small
07 Aug 2015, 15:56
Dan Rees-Jones (2 posts)

Here’s my solution. Could have used the parse methods from the previous example to convert to numbers, but left them out for brevity as I was more interested in how to parse the body of the expression.

defmodule Parse do

  def calculate(expression) do
    do_calculate(expression, '', '', '')
  end

  defp do_calculate([op|t], left, _, _) when op in '+-*/' do
    do_calculate(t, left, List.to_atom([op]), '')
  end

  defp do_calculate([h|t], left, op, right) when h === ?\s do
    do_calculate(t, left, op, right)
  end

  defp do_calculate([h|t], left, '', '') do
    do_calculate(t, [h|left], '', '')
  end

  defp do_calculate([h|t], left, op, right) do
    do_calculate(t, left, op, [h|right])
  end

  defp do_calculate([], left, op, right) do
    x = left  |> Enum.reverse |> List.to_integer
    y = right |> Enum.reverse |> List.to_integer
    apply :erlang, op, [x, y]
  end
end
Generic-user-small
08 Aug 2015, 16:30
Julien Lirochon (2 posts)

I cheated using &List.to_string/1, but here is a solution working with both single and double quoted strings.

defmodule Parse do
  def calculate(formula) when is_list(formula), do: _calculate(List.to_string(formula))
  def calculate(formula), do: _calculate(formula)

  defp _calculate([number1, "+", number2]), do: number1 + number2
  defp _calculate([number1, "-", number2]), do: number1 - number2
  defp _calculate([number1, "*", number2]), do: number1 * number2
  defp _calculate([number1, "/", number2]), do: number1 / number2

  defp _calculate(formula)
  do
    operation = Regex.run(~r/(\d+) ?(\+|-|\*|\/) ?(\d+)/, formula)
    if not is_nil(operation)
    do
      operation
      |> List.delete_at(0)
      |> List.update_at(0, &String.to_integer/1)
      |> List.update_at(2, &String.to_integer/1)
      |> _calculate
    end
  end
end
4c5c2c297ed9f4664cfbe7733a011fb2_pragsmall
22 Aug 2015, 12:33
Artem Medeusheyev (8 posts)

Using Regex.scan

defmodule Printables do
  @reg ~r{[0-9]+|[\-\+\*\/]}
  def calculate(exp) do
    Regex.scan(@reg, List.to_string(exp))
    |> List.flatten
    |> do_calculate
  end
  defp do_calculate([first, op, second]) do
    {f, _} = Integer.parse(first)
    {s, _} = Integer.parse(second)
    case op do
      "+" -> f + s
      "-" -> f - s
      "*" -> f * s
      "/" -> f / s
    end
  end
  defp do_calculate(_), do: raise "Wrong input string format"
end
Generic-user-small
24 Nov 2015, 09:39
Pravin Dahal (4 posts)

My simple solution:

defmodule Exercise do
        def pow(x, 0), do: 1
        def pow(x, n), do: x * pow(x, n - 1)
        def _char_arr2_num([], acc), do: acc
        def _char_arr2_num([head | tail], acc) do
                _char_arr2_num(tail, acc + (head - hd('0')) * pow(10, Enum.count(tail)))
        end
        def char_arr2_num(chars), do: _char_arr2_num(chars, 0)
        def _parse([],            acc), do: char_arr2_num(acc)
        def _parse([?+   | tail], acc), do: char_arr2_num(acc) + _parse(tail, '')
        def _parse([?-   | tail], acc), do: char_arr2_num(acc) - _parse(tail, '')
        def _parse([?*   | tail], acc), do: char_arr2_num(acc) * _parse(tail, '')
        def _parse([?/   | tail], acc), do: char_arr2_num(acc) / _parse(tail, '')
        def _parse([?\s  | tail], acc), do: _parse(tail, acc)
        def _parse([head | tail], acc), do: _parse(tail, acc ++ [head])
        def calculate(expr), do: _parse(expr, '')
end
Generic-user-small
03 Jan 2016, 20:22
Michael Johnston (9 posts)

Mine was pretty similar to Dave’s but I never thought to limit it to one expression, and decided to parse decimal points.


defmodule Calc do
  def calc(str) do
    {left, operations} = _pop_operand(str)
    operations = _skip_spaces(operations)
    _accumulate(left, operations)
  end

  defp _accumulate(value, []) do
    value
  end

  defp _accumulate(left, expression) do
    { operator, rest } = _pop_operator(expression)
    rest = _skip_spaces(rest)
    { right, rest } = _pop_operand(rest)
    rest = _skip_spaces(rest)
    _accumulate(operator.(left, right), rest)
  end

  defp _skip_spaces( [ ?\s | str]), do: _skip_spaces(str)
  defp _skip_spaces(str), do: str

  defp _pop_operand(str), do: _pop_operand(str, 0)
  defp _pop_operand([], value), do: {value, []}
  defp _pop_operand([ digit | tail ], value)
  when digit in ?0..?9 do
    _pop_operand(tail, value*10 + (digit - ?0))
  end
  defp _pop_operand([ ?. | tail ], value) do
    _pop_mantissa(tail, value, 1)
  end
  defp _pop_operand(rest, value), do: {value, rest}
  defp _pop_mantissa([ digit | tail ], value, digits)
  when digit in ?0..?9 do
    _pop_mantissa(tail, value + ((digit - ?0) / :math.pow(10, digits)), digits+1)
  end
  defp _pop_mantissa(rest, value, _), do: {value, rest}

  defp _pop_operator([ ?+ | tail ]), do: { &(&1 + &2), tail}
  defp _pop_operator([ ?- | tail ]), do: { &(&1 - &2), tail}
  defp _pop_operator([ ?* | tail ]), do: { &(&1 * &2), tail}
  defp _pop_operator([ ?/ | tail ]), do: { &(&1 / &2), tail}
  defp _pop_operator(non_operator) do
    raise "Invalid operator '#{[non_operator]}'"
  end
end

Generic-user-small
19 Jan 2016, 02:29
Eduardo Bautista (1 post)

Here is mine:

defmodule Strings do
  def calculate(calculation) do
    _calculate(calculation, 0)
  end

  defp _calculate([], value), do: value
  defp _calculate([ ?+ | tail ], value), do: value + _calculate(tail, 0)
  defp _calculate([ ?- | tail ], value), do: value - _calculate(tail, 0)
  defp _calculate([ ?* | tail ], value), do: value * _calculate(tail, 0)
  defp _calculate([ ?/ | tail ], value), do: value / _calculate(tail, 0)
  defp _calculate([ ?\s | tail ], value), do: _calculate(tail, value)
  defp _calculate([ head | tail ], value) do
    digit = head - ?0
    _calculate(tail, value * 10 + digit)
  end
end
Generic-user-small
22 Jan 2016, 10:17
Sunny Mittal (5 posts)

My solution (not sure on the efficiency, but I think it reads well…also is very particular to the format of the parameter, but I think that’s acceptable based on the question):

def calculate(str) do
  chunked = Enum.chunk_by(Enum.filter(str, &(&1 != 32)), &(&1 in '+-/*'))
  _calculate chunked
end
defp _calculate([ a, '+', b ]), do: List.to_integer(a) + List.to_integer(b)
defp _calculate([ a, '-', b ]), do: List.to_integer(a) - List.to_integer(b)
defp _calculate([ a, '*', b ]), do: List.to_integer(a) * List.to_integer(b)
defp _calculate([ a, '/', b ]), do: List.to_integer(a) / List.to_integer(b)
Generic-user-small
13 Mar 2016, 03:37
Nathan Hessler (10 posts)

My solution expects exactly 1 space between numbers and operater as that is how I read the contract of the char_list to be in the exercise.

defmodule StringMath do

  def calculate(lst) do
    [num1, ' ', action, ' ', num2] = Enum.chunk_by(lst, &(&1 == ?\s))
    _calculate(_get_integer(num1), action, _get_integer(num2))
  end

  defp _calculate(num1, '+', num2), do: num1 + num2
  defp _calculate(num1, '-', num2), do: num1 - num2
  defp _calculate(num1, '*', num2), do: num1 * num2
  defp _calculate(num1, '/', num2), do: div(num1, num2)

  defp _get_integer(number) do
    {int, []} = :string.to_integer(number)

    int
  end
end
Generic-user-small
28 Mar 2016, 04:41
MJ (3 posts)

My simple solution does not use library functions when parsing and accepts flexible input strings.

`def` calculate(expr) when is_list(expr)  do
    unless to_string(expr) =~ ~r/^\s*\d+\s*[\+\-\*\/]\s*\d+\s*$/ do
      false
    else
      {operator, first_operand, second_operand} = parse(expr, {})
      do_calculate operator, first_operand, second_operand
    end
  end

  defp parse([], {o, f, s}), do: {o, f, s}
  defp parse([h | t], res) when h == ?\s, do: parse(t, res)
  defp parse([h | t], {}) when h in ?0..?9, do: parse(t, {nil, h - ?0, 0})
  defp parse([h | t], {nil, f, s}) when h in ?0..?9, do: parse(t, {nil, f * 10 + (h - ?0), s})
  defp parse([h | t], {nil, f, s}) when h in [?+, ?-, ?*, ?/], do: parse(t, {h, f, s})
  defp parse([h | t], {o, f, s}) when h in ?0..?9, do: parse(t, {o, f, s * 10 + (h - ?0)})
                                                             
  defp do_calculate(operator, first_operand, second_operand) when operator == ?+, do: first_operand + second_operand
  defp do_calculate(operator, first_operand, second_operand) when operator == ?-, do: first_operand - second_operand
  defp do_calculate(operator, first_operand, second_operand) when operator == ?*, do: first_operand * second_operand
  defp do_calculate(operator, first_operand, second_operand) when operator == ?/, do: first_operand / second_operand

Generic-user-small
26 Aug 2016, 19:52
Pavlo Osadchyi (1 post)

Here is my solution (highly inspired by strings/parse.exs example)

defmodule Calculator do
  def calculate(str), do: _calculate(str, 0, 0, nil)
  defp _calculate(number1, number2, ?+) do
    number1 + number2
  end
  defp _calculate(number1, number2, ?-) do
    number1 - number2
  end
  defp _calculate(number1, number2, ?*) do
    number1 * number2
  end
  defp _calculate(number1, number2, ?/) do
    number1 / number2
  end
  defp _calculate([?\s|tail], number1, number2, op) do
    _calculate(tail, number1, number2, op)
  end
  defp _calculate([digit|tail], number1, number2, nil)
  when digit in '0123456789' do
    _calculate(tail, number1*10 + digit - ?0, number2, nil)
  end
  defp _calculate([digit|tail], number1, number2, op)
  when digit in '0123456789' do
    _calculate(tail, number1, number2*10 + digit - ?0, op)
  end
  defp _calculate([head|tail], number1, number2, _)
  when head in '/*+-' do
    _calculate(tail, number1, number2, head)
  end
  defp _calculate([_|_], _, _, _) do
    raise "Invalid char"
  end
  defp _calculate([], number1, number2, op) do
    _calculate(number1, number2, op)
  end
end

IO.puts Calculator.calculate('  10 + 20')
Generic-user-small
05 May 2016, 17:51
Sam (6 posts)

Just using concepts learned thus far. Accounts for spaces as well, but expects valid syntax and does not account for division by zero


def calc(l) do
      _calc(l, 0)
  end

  def _calc([h | t], total) when h == ?\s do
    _calc(t, total)
  end

  def _calc([h | t], total) when h == ?+ do
    total + _calc(t, 0)
  end

  def _calc([h | t], total) when h == ?- do
    total - _calc(t, 0)
  end

  def _calc([h | t], total) when h == ?* do
    total * _calc(t, 0)
  end

  def _calc([h | t], total) when h == ?/ do
    total / _calc(t, 0)
  end

  def _calc([h | t], total) do
      _calc(t, total * 10 + h - ?0)
  end

  def _calc([], total) do
      total
  end


Elixir1_pragsmall
10 May 2016, 14:56
Beibut Yerzhanov (9 posts)

my solution is one of the longest, but gets the job done anyway. The solutions from Artem Medeyshev and T.Kort are the best and I like a succinctness of these solutions:

defmodule Calculate do
  
  def calculate(list) do

    sign = list |> Enum.filter(fn(x) -> x == ?+ or x == ?-
    or x == ?* or x == ?/ end)

    case '#{sign}' do
      '+' ->
        value1 = list
        |> Enum.filter(fn(x) -> x != ?\s end)
        |> Enum.take_while(fn(x) -> x != ?+ end)
        |> List.to_integer
        value2 = list
        |> Enum.reverse
        |> Enum.filter(fn(x) -> x != ?\s end)
        |> Enum.take_while(fn(x) -> x != ?+ end)
        |> Enum.reverse |> List.to_integer
        IO.puts("calculate('#{value1} #{sign} #{value2}') # => #{value1+value2}")
      '-' ->
        value1 = list
        |> Enum.filter(fn(x) -> x != ?\s end)
        |> Enum.take_while(fn(x) -> x != ?- end)
        |> List.to_integer
        value2 = list
        |> Enum.reverse
        |> Enum.filter(fn(x) -> x != ?\s end)
        |> Enum.take_while(fn(x) -> x != ?- end)
        |> Enum.reverse |> List.to_integer
        IO.puts("calculate('#{value1} #{sign} #{value2}') # => #{value1-value2}")
      '*' ->
        value1 = list
        |> Enum.filter(fn(x) -> x != ?\s end)
        |> Enum.take_while(fn(x) -> x != ?* end)
        |> List.to_integer
        value2 = list
        |> Enum.reverse
        |> Enum.filter(fn(x) -> x != ?\s end)
        |> Enum.take_while(fn(x) -> x != ?* end)
        |> Enum.reverse |> List.to_integer
        IO.puts("calculate('#{value1} #{sign} #{value2}') # => #{value1*value2}")
      '/' ->
        value1 = list
        |> Enum.filter(fn(x) -> x != ?\s end)
        |> Enum.take_while(fn(x) -> x != ?/ end)
        |> List.to_integer
        value2 = list
        |> Enum.reverse
        |> Enum.filter(fn(x) -> x != ?\s end)
        |> Enum.take_while(fn(x) -> x != ?/ end)
        |> Enum.reverse |> List.to_integer
        IO.puts("calculate('#{value1} #{sign} #{value2}') # => #{value1/value2}")
     end
  end
end
A_pragsmall
24 May 2016, 19:36
Thornton Haag-Wolf (1 post)

My first blind attempt. I wanted to try using some of the various functions I haven’t used yet. It works but looks a bit clunky. It can handle bizarre input such as ‘1adAd2*aaDad D a 1 2’ as long as it filters out to left and right hand operands and a single operator.

defmodule Calculate do
  def calculate(exp) do
    exp #'100 + 45'
    |> Enum.filter(&(&1 >= ?0 && &1 <= ?9 || &1 == ?+ || &1 == ?- || &1 == ?* || &1 == ?/)) #'100+45'
    |> Enum.chunk(1, 1) #['1','0','0','+','4','5']
    |> Enum.chunk_by(&(&1in ['+','-','*','/'])) #[['1','0','0'],['+'],['4','5']]
    |> _calculate
  end

  defp _calculate([left, op, right]) do
    l = :erlang.list_to_integer(List.flatten(left))
    r = :erlang.list_to_integer(List.flatten(right))
    case op do
      ['+'] -> l + r
      ['-'] -> l - r
      ['*'] -> l * r
      ['/'] -> if r > 0 do l / r else raise "Division by zero." end
    end
  end

  defp _calculate(_), do: raise "Invalid input format."
end
Img_2196_pragsmall
25 May 2016, 21:17
Diogo Neves (12 posts)

I understood this problem differently and thought the goal was to support any number of calculations in the same list (e.g. ‘123 + 321 * 2 - 5’)
With that in mind, this is my solution (which solves the asked problem too since it’s a special case of my solution, but I failed to realise it could’ve been simpler)

defmodule Calculator do

  def calculate(str) do
    # removing spaces this way simplifies the structure of the code
    str |> Enum.reject(&(&1 == ?\s)) |> _parse([], 0) |> _compute
  end

  defp _compute([]), do: 0
  defp _compute([value]), do: value
  defp _compute([n1, op, n2 | tail]), do: _compute([op.(n1, n2) | tail])

  defp _parse([], stack, value), do: stack ++ [value]
  defp _parse([digit | tail], stack, value) when digit in '0123456789' do
    _parse(tail, stack, value * 10 + (digit - ?0))
  end
  defp _parse([op | tail], stack, value) when op in '+-*/' do
    op_func = case op do
      ?+ -> &+/2
      ?- -> &-/2
      ?* -> &*/2
      ?/ -> &//2
    end
    _parse(tail, stack ++ [value, op_func], 0)
  end
  defp _parse([invalid|_], _, _) do
    raise "Invalid character #{invalid}"
  end

end

I separated the parsing from the computation because they are two different operations, conceptually.
I also used a case to avoid repeating the recursive call on different pattern matches.

Generic-user-small
29 Jun 2016, 21:12
kovpack (1 post)

I’ve cheated with regex :)

def calculate(list_string) do
  string = list_string |> List.to_string
  reg    = ~r/(?<a>\d+)\s{0,}(?<operator>[-+\/\*]{1})\s{0,}(?<b>\d+)/
  data   = Regex.named_captures(reg, string)

  %{"a" => a, "b" => b, "operator" => operator} = data
  a = a |> String.to_integer
  b = b |> String.to_integer

  case operator do
    "+" -> a + b
    "-" -> a - b
    "*" -> a * b
    "/" -> a / b
  end
end
Generic-user-small
26 Jul 2016, 20:10
Karlo Smid (11 posts)

Regex and result pattern matching:

def calculate(expression) do
    case Regex.named_captures(~r/(?<first>[123456789]\d*)\s*(?<operand>[\+-\\*\/])\s*0*(?<second>[123456789]\d*)/, expression) do
      %{"first" => a, "operand" => "+", "second" => b} ->
        elem(Integer.parse(a),0) + elem(Integer.parse(b),0)
      %{"first" => a, "operand" => "-", "second" => b} ->
        elem(Integer.parse(a),0) - elem(Integer.parse(b),0)
      %{"first" => a, "operand" => "*", "second" => b} ->
        elem(Integer.parse(a),0) * elem(Integer.parse(b),0)
      %{"first" => a, "operand" => "/", "second" => b} ->
        elem(Integer.parse(a),0) / elem(Integer.parse(b),0)
      %{"first" => _, "operand" => non_support, "second" => _} ->
        raise "Not supported operand '#{non_support}'"
      _ -> raise "Not parsable input"
    end
  end
Remy_pragsmall
29 Jul 2016, 06:35
Rémy Coutable (2 posts)

I had a very natural approach:

  • a function to find each term
  • a function to find the operator
  • a function that does the calculation

I believe this ended-up in an easy to understand module. I chose to use the functions we’ve seen so far in the book:

defmodule StringsAndBinaries.Exercise4 do
  def calculate(expression) do
    _calculate(term(expression, :left), term(expression, :right), operator(expression))
  end

  defp term(expression, position_sym) do
    terms = Enum.chunk_by(expression, &(&1 in '?\s+-*/'))
    position = case position_sym do
      :left -> 0
      _ -> -1
    end

    terms
    |> Enum.at(position)
    |> List.to_integer
  end

  defp operator(expression) do
    Enum.partition(expression, fn(x) -> x in '+-*/' end)
    |> Tuple.to_list
    |> Enum.at(0)
  end

  defp _calculate(term1, term2, operator) do
    case operator do
      '+' -> term1 + term2
      '-' -> term1 - term2
      '*' -> term1 * term2
      '/' -> term1 / term2
    end
  end
end

128 = StringsAndBinaries.Exercise4.calculate('32+96')
128 = StringsAndBinaries.Exercise4.calculate('32 + 96')
128 = StringsAndBinaries.Exercise4.calculate('256-128')
128 = StringsAndBinaries.Exercise4.calculate('256 - 128')
128 = StringsAndBinaries.Exercise4.calculate('32*4')
128 = StringsAndBinaries.Exercise4.calculate('32 * 4')
128.0 = StringsAndBinaries.Exercise4.calculate('512/4')
128.0 = StringsAndBinaries.Exercise4.calculate('512 / 4')
Generic-user-small
07 Aug 2016, 20:31
Robert Chrzanowski (4 posts)
defmodule Parse do

  def calculate(l) do
    [root] = l
    |> _tokenize
    |> _parse_multiplication([])
    |> Enum.reverse
    |> _parse_addition([])
    _calculate(root)
  end

  defp _tokenize([]),  do: []
  defp _tokenize([?\s | rest]), do: _tokenize(rest)
  defp _tokenize([?+ | rest]), do: [:add | _tokenize(rest)]
  defp _tokenize([?- | rest]), do: [:subtract | _tokenize(rest)]
  defp _tokenize([?* | rest]), do: [:multiply | _tokenize(rest)]
  defp _tokenize([?/ | rest]), do: [:divide | _tokenize(rest)]
  defp _tokenize([digit | rest])
  when digit in '123456789' do _number_token(rest, digit - ?0) end

  defp _number_token([digit | tail], number)
  when digit in '0123456789' do _number_token(tail, number * 10 + digit - ?0) end
  defp _number_token(rest, number), do: [{:number, number} | _tokenize(rest)]

  defp _parse_multiplication([], col), do: col
  defp _parse_multiplication([left, binary_op, right | rest], col)
  when binary_op in ~w(multiply divide)a do
    _parse_multiplication(rest, [{binary_op, left, right} | col])
  end
  defp _parse_multiplication([right | rest], col), do: _parse_multiplication(rest, [right | col])

  defp _parse_addition([], col), do: col
  defp _parse_addition([left, binary_op, right | rest], col)
  when binary_op in ~w(add subtract)a do
    _parse_addition(rest, [{binary_op, left, right} | col])
  end
  defp _parse_addition([right | rest], col), do: _parse_addition(rest, [right | col])

  defp _calculate({:number, num}), do: num
  defp _calculate({:add, left, right}), do: _calculate(left) + _calculate(right)
  defp _calculate({:subtract, left, right}), do: _calculate(left) - _calculate(right)
  defp _calculate({:multiply, left, right}), do: _calculate(left) * _calculate(right)
  defp _calculate({:divide, left, right}), do: _calculate(left) / _calculate(right)

end
Generic-user-small
26 Aug 2016, 22:07
Zachery Kayser (2 posts)

I borrowed the number parsing from the previous example and used a Regex to extract the operator. The Regex makes the naive assumption that there are spaces between the operands and operator.

def calculate(math) do
    str = Kernel.to_string(math)
    split = Regex.split(~r/ /, str)
    ( convert = split 
      |> Enum.map(&(Kernel.to_charlist(&1)))
      |> Enum.into([])
      |> Enum.map(&(Characters.number(&1)))
    )
    perform_operation(convert)
  end
  
  def number(operator) when operator in ['+', '-', '*', '/'], do: operator
  def number([?-|tail]), do: _number_digits(tail, 0) * -1
  def number([?+|tail]), do: _number_digits(tail, 0)
  def number(str), do: _number_digits(str, 0)
  
  defp _number_digits([], value), do: value
  defp _number_digits([ digit | tail], value) when digit in '0123456789' do
    _number_digits(tail, value * 10 + (digit - ?0))
  end
  defp _number_digits([non_digit|_], _) do
    raise "Invalid digit #{[non_digit]}"
  end
  
  def perform_operation([num1, '+', num2]), do: num1 + num2
  def perform_operation([num1, '-', num2]), do: num1 - num2
  def perform_operation([num1, '*', num2]), do: num1 * num2
  def perform_operation([num1, '/', num2]), do: num1 / num2
Generic-user-small
06 Sep 2016, 11:03
Holger Schmeisky (4 posts)

Reading the other solutions I figured out I fell back to thinking in Python. Thanks for this forum!

defmodule Parse do

#http://www.elixre.uk/ to build regexes
        @term_regex ~r/([0-9]+)\s*([\+\-\*\/])\s*([0-9]+)/
        @char_to_operation %{"+" => &Kernel.+/2,
                                                 "-" => &Kernel.-/2,
                                                 "*" => &Kernel.*/2,
                                                 "/" => &Kernel.//2}

        # requires a chr list as argument! string does not match
        def number(str), do: _parse_number(str, 0)

        defp _parse_number([], value), do: value

        defp _parse_number([ digit | tail], value) 
        when digit in ?0..?9 do
                _parse_number(tail, value* 10 + digit - ?0)        
        end

        defp _parse_number([ non_digit | _ ], _) do
                IO.puts "invalid digit #{[non_digit]}."
        end

        def calculate(str) do
                if !Regex.match?(@term_regex, str) do
                        raise "Input string in wrong format, was '#{str}', should match '#{Regex.source(@term_regex)}'"
                end
                # scan finds all matches, does not stop after first pass
                [[a, op, b]] = Regex.scan(@term_regex, str, [capture: :all_but_first])
                @char_to_operation[op].(number(to_charlist a), number(to_charlist b))
        end
end

Generic-user-small
06 Sep 2016, 11:11
Holger Schmeisky (4 posts)

Combining the solutions from Karlo Smid and Daniel Ashton is really concise:

def calculate_by_regex_match(expression) do
            case Regex.named_captures(~r/(?<first>[0-9]\d*)\s*(?<operand>[\+-\\\/])\s*0*(?<second>[0-9]\d*)/, expression) do
              %{"first" => a, "operand" => op, "second" => b} ->
                apply :erlang, String.to_atom(op), [String.to_integer(a), String.to_integer(b)]
              _ -> raise "Not parsable input, "
            end
        end
You must be logged in to comment