問題5 の elixir版

http://www.softantenna.com/wp/software/5-programming-problems/
1時間以内に解けなければプログラマ失格となってしまう5つの問題が話題に

問題5
1,2,…,9の数をこの順序で、”+”、”-“、またはななにもせず結果が100となるあらゆる組合せを出力するプログラムを記述せよ。例えば、1 + 2 + 34 - 5 + 67 - 8 + 9 = 100となる
をelixirでやってみる。

    • -

● お答え。17行で答が出る。

defmodule Make do
  def make([],[h|t]) do # 始め
    make( [h] , t )
  end
  def make(prefix,[]) do  # 最後
    case Code.eval_string(prefix) do
      {100, _} -> [ prefix ]
      _        -> [ ]
    end
  end
  def make(prefix,[h|t]) do # 途中
    make( prefix ++ '+' ++ [h] , t )  ++
    make( prefix ++ '-' ++ [h] , t )  ++
    make( prefix     ++    [h] , t )
  end
end

Make.make([],'123456789')
    • -

● iex に放り込んで実行してみる。

[tk@NA501-33 ~]$ iex
Erlang/OTP 17 [erts-6.4.1] [source] [64-bit] [async-threads:10] [kernel-poll:false]
Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)

iex(2)> defmodule Make do
...(2)> def make([],[h|t]) do # 始め
...(2)>   make( [h] , t )
...(2)> end
...(2)> def make(prefix,[]) do  # 最後
...(2)>   case Code.eval_string(prefix) do
...(2)>     {100, _} -> [ prefix ]
...(2)>     _        -> [ ]
...(2)>   end
...(2)> end
...(2)> def make(prefix,[h|t]) do # 途中
...(2)>   make( prefix ++ '+' ++ [h] , t )  ++
...(2)>   make( prefix ++ '-' ++ [h] , t )  ++
...(2)>   make( prefix     ++    [h] , t )
...(2)> end
...(2)> end

{:module, Make, <<70, 79,  ...>>, {:make, 1}}

iex(4)> Make.make([],'123456789')
['1+2+3-4+5+6+78+9', '1+2+34-5+67-8+9', '1+23-4+5+6+78-9', '1+23-4+56+7+8+9',
 '12+3+4+5-6-7+89', '12+3-4+5+67+8+9', '12-3-4+5-6+7+89', '123+4-5+67-89',
 '123+45-67+8-9', '123-4-5-6-7+8-9', '123-45-67+89']

何をやっているかというと・・・、

3個の make/2 関数はすべて、リスト文字列の prefix と rest を受け取って、条件にマッチしたリスト文字列のリストを返す、という点で共通。
make/2関数は再帰的に呼ばれる。最初はprefix=''、restは全部。呼ばれるに従ってprefixが延びていき、restは短くなっていく。

「途中」の「make(prefix,[h|t])」関数はprefix文字列の最後に、「+」「-」「」を挟んで、rest文字列の先頭の文字(h)を追加して、下位のmake/2関数を呼び出す。「+」「-」「」を挟むので3回呼び出して、それぞれが返して来た文字列のリストを合成して返す。

「最後」の「make(prefix,[])」関数は、受け取った prefix が条件にマッチした場合には、その文字列が一つだけ入ったリストを返す。マッチしない場合には空リストを返す。もしマッチ判断をせずに全て返した場合には、最初の make/2 関数は3の8乗(=6561)個の文字列のリストを返すことになる。

    • -

◎ elixir って、超おもしろい。