Yコンビネータ

id:sshiさんにブクマで「これってYコンビネータ?」とコメントを頂いたので、考えてみました。
とりあえず、

  1. カリー化されてない
  2. 美しくない

という理由で、Yコンビネータではないみたいです。
で、Yコンビネータに持っていけるかやってみました。
ほとんど「再帰的な関数」を手続きオブジェクトにする - バリケンのRuby日記 - Rubyistをそのままなぞりました。そっちのエントリを見たほうがいいと思います。
前回、出来たコードは

fact10 =
lambda{|f|
  lambda{|n|
    f.call(f, n)
  }
}.call(
lambda {|slf, m| 	# factorial
    if m == 0
      1
    else
      m * slf.call(slf, m-1)
    end
}
).call(10)

p fact10

でした。とりあえずカリー化します。変数名は変えました。

y =
lambda{|f|
  lambda{|n|
    f[f][n]
  }
}

h =
lambda{|u|
  lambda{|m|
    if m == 0
      1
    else
      m * u[u][m-1]        # dirty!
    end
  }
}

fact = y[h]
p fact[10]

1のカリー化はクリアしましたが、2が顕在化しました。hの中のu[u][m-1]が美しくない。できればu[m-1]と書けたほうが人に優しい。
そこで、hを等価に変えていきます。利用するのはflambda{|x| f[x]}が等価だということ。
hに等価なgを作ります。カリー化されてるので、2変数関数でも気にしなくていいのが気分いい。

y =
lambda{|f|
  lambda{|n|
    f[f][n]
  }
}

# hと同値なg
g =
lambda{|u|
  lambda{|v|
    h = lambda{|slf|
      lambda{|m|
        if m == 0
          1
        else
          m * slf[slf][m-1]        # dirty!
        end
      }
    }
    h[u][v]
  }
}

fact = y[g]
p fact[10]

まだいやなところは残ってます。slfには、h[u][v]の形でuが実引数で渡されてます。uはそれ以外に使われてないので、h[u][v]段階でuの代わりにu[u]としてやれば、slf[slf]は、slf一つで済みます。

y =
lambda{|f|
  lambda{|n|
    f[f][n]
  }
}

# u <-> slfの呼び出し関係を修正
g =
lambda{|u|
  lambda{|v|
    h = lambda{|slf|
      lambda{|m|
        if m == 0
          1
        else
          m * slf[m-1]
        end
      }
    }
    h[u[u]][v]
  }
}

fact = y[g]
p fact[10]

hの定義部分のlambdaは独立させられるので、外へ。

y =
lambda{|f|
  lambda{|n|
    f[f][n]
  }
}

# くくりだした
h = lambda{|slf|
  lambda{|m|
    if m == 0
      1
    else
      m * slf[m-1]
    end
  }
}

g =
lambda{|u|
  lambda{|v|
    h[u[u]][v]
  }
}

fact = y[g]
p fact[10]

最後のfact = y[g]で、yにはhを与えれば良いようにgを入れ込む。
長くなって見にくいので、yのなかで変数を使いました。

# yが直接hを受け取れるように修正
y =
lambda{|q|
  p = lambda{|f|
    lambda{|n|
      f[f][n]
    }
  }
  g = lambda{|u|
    lambda{|v|
      q[u[u]][v]
    }
  }
  p[g]
}

h =
lambda{|slf|
  lambda{|m|
    if m == 0
      1
    else
      m * slf[m-1]
    end
  }
}

fact = y[h]
p fact[10]

これで、当初の美しくない部分がすっきりしました。

うーん、「再帰的な関数」を手続きオブジェクトにする - バリケンのRuby日記 - RubyistのYコンビネータと形が違うなぁ。いいのかな?

sum_sub =
lambda{|s|
  lambda{|n|
    if n == 0
      0
    else
      n + s[n-1]
    end
  }
}
sum = y[sum_sub]
p sum[10]    # 55

まあ、よしとしよう。
それと、ちょっと理解できたことがあるけど、また明日にでも。