てきとう

てきとう

Tclでλ計算(をしようとして失敗)

Tclでλ計算ができると聞いたので試してみました。
しかし、まずTclを思い出す作業から始めないといけないのでスタートラインが遠すぎる…。


注意:
Tclのapplyに関しての情報は、

等を読む事を*強く*お勧めします。この日記はチラシの裏以下の価値しかありません。

ここから思い出しメモ。

  1. Tclでは全てが文字列である。
  2. Tclはスクリプト言語なので一行づつ処理される。
  3. プログラムは、与えられた文字列中を半角スペースで区切った最初の文字列をコマンド名として、残りの文字列を引数として実行される。
  4. {}内はひとまとまりの文字列として扱われる。
  5. []内は計算結果に置換される。
  6. 変数は頭に'$'を付けて区別。*1

…まぁ文法はこんな感じだったはず。

hoge fuga

ならhogeを、fugaを引数として呼び出す。

expr 1+1

exprは引数を数式と見なして計算する。この場合の結果は「2」。

if {$n-2==0} {hoge} else {fuga}

ifすら構文には含まれない。コマンドの一つ。
最初の引数({$n==0})をexprで計算した結果が、

  1. 「0以外」なら次の文字列({hoge})、
  2. 「0」ならelseの次の文字列({fuga})を実行する。*2
proc hoge {fuga} {
    expr {$fuga*$fuga}
}
hoge 42

関数はこんな感じ。procはやはり単なるコマンド。
hogeは1-arityの関数で、引数^2を計算する。途中に改行が含まれるが、{}内なので問題なし。*3

ここから本題。

さて、Tcl 8.5でapplyが追加されました。
引数に取った文字列を解釈して実行してくれる、素晴らしい関数です。

  • 一つ目の引数には処理する内容を書きます。{arg_list body}の形です。
    • arg_listは引数のリストです。
    • bodyは実際の処理です。arg_listで宣言した引数を使う事が出来ます。
  • 二つ目以降の引数には実引数を書きます。arg_listとマッチするように与える必要があります。

つまり、procの関数名より後ろをまとめて書けばOKです。
例えば、

apply {x {expr $x*$x}} 42 

は42^2を計算します。
ここで、{x {expr $x*$x}}は(というかTclプログラムすべては)単なる文字列ですので、変数に格納できます。

set sqr {x {expr $x*$x}}

$sqrを引数として、applyを呼び出す事ができます。

apply $sqr 42

は1764を返します。

以上でapplyの説明終了。

本題の本題

という事は、似非高階関数が使えるという事になります。

set sqr {x {expr $x*$x}}
proc apply_42 {f} {
    apply $f 42
}
apply_42 $sqr
proc get_sqr {} {
    list x {expr $x*$x}
}
apply [get_sqr] 42

が、ここで問題発生。

set plus {{x} {list y {expr $x+$y}}}
apply [apply $plus 1] 2

"3"を期待したのですが、"can't read "x": no such variable"とエラーが返ってきてしまいました。
試しに

set p [apply $plus 1]

するとpは「y {expr $x+$y}」…

…あ、そうか。
「{}」内は文字列だから変数表記も置換されないのでした。
これを回避する方法があったような気もする*4のですが、思い出せない上にそろそろ時間切れ。*5
誰か日本語でちゃんとした記事書いてくれないかなぁ、と他力本願しておきます。

*1:$自体はsetコマンドの省略、だった気がする。

*2:elseは省略可能で、"if {$n==0} {hoge} {fuga}"とも書ける。

*3:expr
{$fuga*$fuga}はNG。

*4:format(だっけ?)を通すのも一つの手かも。

*5:Tclの文法思い出すのに時間をかけすぎた…。