Need help understanding Parser eg from Programming Elixir book

strings/parse.exs
defmodule Parse do
  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 
end

Let’s try it in iex.
iex> c("parse.exs") [Parse]
iex> Parse.number('123') 123
iex> Parse.number('-123') -123
iex> Parse.number('+123') 123

So the above code is from the book Programming Elixir Page (118) ebook version. I have typed and run the script and it works as expected however I get lost when I try to walk through the code by hand. So the following is my reasoning,

Using the eg Parse.number('123')
At the first pass, the first two def number patterns fail since the list of characters doesn’t start with - or +. The third def number matches do: _number_digits(str, 0) causing _number_digits([ digit | tail ], value) to be executed.

At this point, still in the first pass, digit = '1', tail = '23' and value = 0. Since digit is in '0123456789' the when code is executed where _number_digits is recursively called.

I think the call is made with the following arguments _number_digits( '23', 0*10 + 1 - 48) where 48 comes from ?0, simplyfing to _number_digits( '23', -47).

The second pass follows the same route as the first leading to another recursive call of _number_digits( '3', -47*10 + 2 - 48). Clearly this isn’t going to give me the result that I expect i.e. 123.

Where am I going wrong in my understanding? Thanks.

2 Likes

The first time you run into this, digit is ?1 (49), not 1. You have to remember that '123' is a shorthand for [?1, ?2, ?3], which again is convinience for [49, 50, 51].

4 Likes

Wrong here, it is not 0+10 + 1 - 48, remember the '1' is a character, not an integer 1, and ?1 is 49, thus it is 0+10 + 49 - 48. It is just ASCII. :slight_smile:

EDIT: Er, yes, prior post…

1 Like