15 Jul 2013, 03:42
Dave_gnome_head_isolated_pragsmall

Dave Thomas (338 posts)

  • The Elixir test framework, ExUnit, uses some clever code quoting tricks. For example, if you assert

    assert 5 < 4
    

    You’ll get the error “expected 5 to be less than 4.”

    The Elixir source code is on Github (at

    https://github.com/elixir-lang/elixir

    ). The implementation of this is in the file /lib/ex_unit/lib/assertions.ex. Spend some time reading this file, and work out how it implements this trick.

    (Hard) Once you’re done that, see if you can use the same technique to implement a function that takes an arbitrary arithmetic expression and returns a natural language version.

    explain do: 2 + 3*4
    #=> multiply 3 and 4, then add 2
    
18 Aug 2014, 16:18
Mac 128k logic board 96x96_pragsmall

Roger Turner (7 posts)

explainer.ex

  defmodule Explainer do

    # A simple "natural language" form is a uniform sequence of verb-initial clauses:
    # - implies clauses like "subtract right from left", and similarly for other ops;
    # - "by" parameter used for "multiply by <number>" (compare "subtract <number>").

    defmacro make(op, name, from, by) do
      quote do
        def explainer({unquote(op), _, [left, right]}) 
            when is_number(left) and is_number(right) do
          unquote(name) <> "#{right} " <> unquote(from) <> "#{left}"
        end
        def explainer({unquote(op), _, [left, right]}) 
            when is_number(left) do
          "#{explainer right}, then " <> unquote(name) <> unquote(from) <> "#{left}"
        end
        def explainer({unquote(op), _, [left, right]}) 
            when is_number(right) do
          "#{explainer left}, then " <> unquote(name) <> unquote(by) <> "#{right}"
        end
        def explainer({unquote(op), _, [left, right]}) do
          "#{explainer left}, then #{explainer right}, then " 
          <> String.rstrip(unquote(name))
        end
      end
    end
  end

explain.ex

  defmodule Explain do

    require Explainer

    Explainer.make(:+, "add ",      "to ",   "")
    Explainer.make(:-, "subtract ", "from ", "")
    Explainer.make(:*, "multiply ", "by ",   "by ")
    Explainer.make(:/, "divide ",   "into ", "by ")

    defmacro explain(n) when is_number(n), do: "#{n}"

    defmacro explain {op, _, [left,right]} do
      explainer {op, nil, [left, right]}
    end

  end

  # explain 2 + 3 * 4 
  # => "multiply 4 by 3, then add 2”

  # explain (2+3)/(4-5*6) 
  # => "add 3 to 2, then multiply 6 by 5, then subtract from 4, then divide"
  You must be logged in to comment