問題5のelixir版をruby版に翻訳してみた。

http://d.hatena.ne.jp/take_tk/20150710 で作った。
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版をrubyに翻訳してみる。

    # prefix は文字列、rest は文字の配列
    # 文字列の配列を返す。
    #
  def make(prefix,rest=nil)
      # make/1
    return make("", prefix.split(//)) if rest.nil?
      # make/2
    h = rest[0]
    t = rest[1..-1]
    case
      when prefix == "" # 始め
        return make( h , t )
      when rest == []   # 最後
        if eval(prefix) == 100
          return [ prefix ]
        else
          return []
        end
      else              # 途中
        return make( prefix + '+' + h , t ) +
               make( prefix + '-' + h , t ) +
               make( prefix    +    h , t )
    end # case
  end

p( 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"]

確かに、rubyでも実行可能だ。elixirと同様にコンパクトだ。しかし…。

不安感が爆発するね。「これでよい」という安心感が全くない。

というより、何をやっているのかも理解不能だ。
そもそも、ruby脳で、これを作れと言われても作れないだろう。

何故混乱するかというと、
まったく異なった処理を行なうメソッドが、同じメソッドの中に入っている、というのが理解不能/不安の原因だな。

elixirの場合には引数のパターンごとに別の関数になっているので、その関数が呼ばれる場合だけを想定して理解すればよい。

ということで、ruby版で場合分けを行なうメソッドを分離してみた。elixir版とそっくりになった。

    # prefix は文字列、rest は文字の配列
    # 文字列の配列を返す。
    #
  def make(prefix,rest=nil)
      # make/1
    return make_1(prefix) if rest.nil?
      # make/2
    h = rest[0]
    t = rest[1..-1]
    case
      when prefix == "" # 始め
        return make_first(prefix,h,t)
      when rest == []   # 最後
        return make_last(prefix,rest)
      else              # 途中
        return make_mid(prefix,h,t)
    end # case
  end

  def make_1(prefix)
    make("", prefix.split(//))
  end

  def make_first(prefix,h,t)  # when prefix == ""
    make( h , t )
  end
# ↑ ruby版 ⇔ elixir版 ↓
# def make([],[h|t]) do # 始め
#    make( [h] , t )
#  end

  def make_last(prefix,rest)   # when rest == []
    case eval(prefix) 
      when 100 ;  [ prefix ]
      else     ;  []
    end
  end
# ↑ ruby版 ⇔ elixir版 ↓
#  def make(prefix,[]) do  # 最後
#    case Code.eval_string(prefix) do
#      {100, _} -> [ prefix ]
#      _        -> [ ]
#    end
#  end

  def make_mid(prefix,h,t)    # else
    make( prefix + '+' + h , t ) +
    make( prefix + '-' + h , t ) +
    make( prefix    +    h , t )
  end
# ↑ ruby版 ⇔ elixir版 ↓
#  def make(prefix,[h|t]) do # 途中
#    make( prefix ++ '+' ++ [h] , t ) ++
#    make( prefix ++ '-' ++ [h] , t ) ++
#    make( prefix     ++    [h] , t )
#  end

p( 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"]

このパターンで分解すると、問題が簡単になる、というケースは多いのだろうか?