6.3. kink/BINDING¶
6.3.1. binding型¶
binding値、つまり束縛は、局所変数の所有者である。
プログラム中で `\binding` を使うことで、現在の関数呼び出しの束縛が得られる。
:foo <- {
:Num = 42
stdout.print_line(\binding.Num.repr)
}
foo # => 42
新しく束縛を作り、それを使ってプログラムを評価することができる。
:BINDING.require_from('kink/')
:PROGRAM.require_from('kink/program/')
:Binding <- BINDING.new
Binding:Num <- 42
:Program <- PROGRAM.new('program' 'Num * 2')
:eval_program <- Program.compile{(:C)
C.binding(Binding)
}
stdout.print_line(eval_program.repr) # => 84
すべての束縛は、以下に挙げる関数、つまりnew_valやstdoutなどを局所変数として持つ。これらの関数は、「事前ロード済み関数」と呼ばれる。
6.3.1.1. nada¶
「青い空に金属の看板がキーキー鳴って、nada、nada。それで叫んで飛び起きたってわけ」 ―― 競売ナンバー49の叫び(1966年, トマス・ピンチョン)
nadaは無意味な値の定数である。nada値は、使う意味のある値が他にないときに使われる。たとえば、変数参照の `<-` 演算子は、戻す意味のある値が他にないので、nadaを戻す。
stdout.print_line((:X <- 49).repr) # => nada
6.3.1.2. true¶
trueはbool型の真値の定数である。
6.3.1.3. false¶
falseはbool型の偽値の定数である。
6.3.1.4. new_val(...[Sym0 Val0 Sym1 Val1 ,,,])¶
new_valは新しい値を戻す。引数が渡された場合、new_valは、作った値の変数への値の代入も行う。
変数代入
Oを作った値、Nを非負の整数、 Sym(0), Val(0), Sym(1), Val(1) ,,, Sym(N - 1), Val(N - 1) を渡された引数とする。
0から `N - 1` までの整数 i について昇順で、new_valは、変数 (owner=O, symbol=Sym(i)) に対し、 Val(i) を内容として、変数格納操作を実行する。
事前条件
引数の数は偶数でなければならない。
Sym0, Sym1 ,,, はstr値でなければならない。
例
:V <- new_val(
'bark' { 'Bow' }
'howl' { 'Wow' }
)
stdout.print_line(V.bark) # => Bow
stdout.print_line(V.howl) # => Wow
上記のプログラムは次と同じ意味である。
:V <- new_val
V:bark <- { 'Bow' }
V:howl <- { 'Wow' }
stdout.print_line(V.bark) # => Bow
stdout.print_line(V.howl) # => Wow
6.3.1.5. raise(Msg)¶
raiseは、Msgを例外メッセージとして、現在のスタックトレースを例外のスタックトレースとして、例外を投げる。
事前条件
Msgはstrでなければならない。
6.3.1.6. ! Bool / op_lognot(Bool)¶
`!` 演算子、つまりop_lognot関数は、Boolの論理否定を戻す。
Boolがtrueなら、op_lognotはfalseを戻す。
Boolがfalseなら、op_lognotはtrueを戻す。
事前条件
Boolはboolでなければならない。
6.3.1.7. Bool1 || Bool2 / op_logor(Bool1 $thunk={ Bool2 })¶
`||` 演算子、つまりop_logor関数は、短絡評価で論理和操作を行う。
Bool1がtrueなら、op_logorはtrueを戻す。
Bool1がfalseなら、op_logorは$thunkを末尾呼び出しする。
事前条件
Bool1はboolでなければならない。
6.3.1.8. Bool1 && Bool2 / op_logand(Bool1 $thunk={ Bool2 })¶
`&&` 演算子、つまりop_logand関数は、短絡評価で論理積操作を行う。
Bool1がtrueなら、op_logandは$thunkを末尾呼び出しする。
Bool1がfalseなら、op_logandはfalseを戻す。
事前条件
Bool1はboolでなければならない。
6.3.1.9. if(Bool $true_cont ...[$false_cont={}])¶
ifは、一方向か二方向の分岐を行う。
Boolがtrueなら、ifは$true_contを末尾呼び出しする。
Boolがfalseなら、ifは$false_contを末尾呼び出しする。
事前条件
Boolはboolでなければならない。
$true_contはサンクでなければならない。
$false_contはサンクでなければならない。
例: 一方向分岐
:print_if_even <- {(:N)
if(N % 2 == 0){
stdout.print_line('even number {}'.format(N.repr))
}
}
print_if_even(8) # => even number 8
print_if_even(11) # => (no output)
例: 二方向分岐
:compare <- {(:Num)
if(Num < 100
{ 'less than 100' }
{ 'greater than or equal to 100' }
)
}
stdout.print_line(compare(50)) # => less than 100
stdout.print_line(compare(200)) # => greater than or equal to 100
6.3.1.10. branch(...[$cond0 $then0 $cond1 $then1 ,,,])¶
branchは複数方向分岐を行う。
まずbranchは、$cond0, $cond1 ,,, を引数なしで、どれかがtrueを戻すまで昇順で呼び出す。どれもtrueを戻さなければ、branchは例外を投げる。
ついでbranchは、trueを戻したcondサンクに対応するthenサンクを、引数なしで末尾呼び出しする。
事前条件
引数の数は偶数でなければならない。
$cond0, $cond1 ,,, はboolを戻すサンクでなければならない。
$then0, $then1 ,,, はサンクでなければならない。
例
:Sorted_nums <- [1 2 3 5 8 11 19 30 49 89]
# returns whether Sorted_nums contains N, by binary search algorithm
:contain? <- {(:N)
:in_range? <- {(:From :To)
From < To && (
:Ind = (From + To) // 2
:T = Sorted_nums.get(Ind)
branch(
{ T < N } { in_range?(Ind + 1 To) }
{ T > N } { in_range?(From Ind) }
{ true } { true }
)
)
}
in_range?(0 Sorted_nums.size)
}
stdout.print_line(contain?(30).repr) # => true
stdout.print_line(contain?(42).repr) # => false
6.3.1.11. stdin¶
stdinは、ランタイムの標準入力を表すscanner+inputの定数である。
scannerとしてのstdinは、CHARSET.defaultを使ってバイト列をデコードする。
例: stdinからすべて読んで、stdoutに書き出す
:Bin <- stdin.read_all
stdout.write(Bin)
例: デフォルトのcharsetで cat -n
:loop <- {(:N)
stdin.scan_line{(:C)
C.on_success{(:Line)
stdout.print("{}\t{}".format(N Line))
loop(N + 1)
}
C.on_eof{}
}
}
loop(1)
例: UTF-8の入力、出力で cat -n
:INPUT_SCANNER.require_from('kink/io/')
:OUTPUT_PRINTER.require_from('kink/io/')
:CHARSET.require_from('kink/')
:Scanner <- INPUT_SCANNER.new(stdin CHARSET.utf8)
:Printer <- OUTPUT_PRINTER.new(stdout CHARSET.utf8 "\n")
:loop <- {(:N)
Scanner.scan_line{(:C)
C.on_success{(:Line)
Printer.print("{}\t{}".format(N Line))
loop(N + 1)
}
C.on_eof{}
}
}
loop(1)
6.3.1.12. stdout¶
stdoutは、ランタイムの標準出力を表すprinter+outputの定数である。
printerとしてのstdoutは、改行のstrとしてSTR.default_newlineを使い、CHARSET.defaultを使って文字列をエンコードする。
6.3.1.13. stderr¶
stderrは、ランタイムの標準エラー出力を表すprinter+outputの定数である。
printerとしてのstderrは、改行のstrとしてSTR.default_newlineを使い、CHARSET.defaultを使って文字列をエンコードする。
stderrのwrite, print, print_line, flush, closeの各メソッドは同期化される。これは、複数のスレッドがstderrにエラーメッセージを出力できるようにするためである。
print_xxxメソッドでエンコーディングエラーが起きたら、stderrは、IOエラーを発生させるのではなく、代替バイト列を書き出す。これは、stderrに書き出された文字列そのものの方が、たとえ不完全であっても、変換エラーのエラーメッセージよりも有用だからである。
6.3.2. BINDING.new¶
newは、新しいbindingを戻す。
6.3.3. BINDING.is?(Val)¶
is?は、Valがbinding値であるかどうかを戻す。