English | Japanese SourceForge.JP
Copyright (c) 2011-2012 Yutaka Saito

ブロック式

制御構文などの表記を可能にするため、ブロック式の概念を導入しています。 if や while などの構文や、クラス宣言、リスト・辞書要素の初期化表記も、すべてこの機構を使って実装されています。

f(x:number) {block} = {
    block(x)
    block(x + 1)
    block(x + 2)
}

呼び出し方は以下のようになります。

f(2) {|x|
    print(x)
} # 234

ブロック自体は特殊な変数スコープを持たせた関数オブジェクトで、ブロックパラメータは単に関数への引数になります。ブロックパラメータの操作が周囲に影響を与えることはありません。また、通常の関数オブジェクトで定義できる引数設定 (オプション引数・デフォルト値・可変長引数など) は、ブロックパラメータでも有効です。

x = 0
f(2) {|x|         # dummy arguments are completely isolated from outside
    print(x)
}
println(x)        # 0

ブロックを表すシンボルは何でもかまいません。

f(x:number) {yield} = {
    yield(x)
    yield(x + 1)
    yield(x + 2)
}

シンボルの後にクエスチョンマークをつけたブロックは、オプショナルとして扱われます。この場合、ブロックをつけないで関数を実行すると nil に設定されます。

f_opt() {block?} = {
    if (block == nil) {
        println('not specified')
    } else {
        block()
    }
}

f_opt() # print 'not specified'

f_opt() {
    println('message from block')
}       # print 'message from block'

ブロックは通常、それを実行している関数の「外側」の環境にアクセスできる変数スコープで動作します (「外側」とはレキシカルスコープのそれになりますが、ダイナミックスコープに切り替えることもできます)。関数外部の変数の値を変更することができます。

g() {block} = {
    block()
}

n = 2
g() {
    n = 5
}
printf('n = %d\n', n) # n = 5

ブロックシンボルにアトリビュート :inside_scope をつけると、関数内部のスコープに切り替わり、関数の内部処理で設定される変数などにアクセスできるようになります。関数外部の変数は、参照できた値に対しての改変が可能です。

h() {block:inside_scope} = {
    m = 'local in h()'
    block()
}

h() {
    printf('%s\n', m)    # h()'s local variable m is accessible
}

n = 2
h() {
    n = 5
}
printf('n = %d\n', n)    # n = 2

h() {
    n += 5
}
printf('n = %d\n', n)    # n = 7

quoted value にしたブロックを設定した変数を {|..|} で囲って関数に渡すと、それがブロック本体として扱われます。ブロックパラメータも記述できます。例えば:

f() {block} = {
    block(1, 2, 3, 4)
}

という関数があった場合、以下のふたつの呼び出しは等価です。

block = `{|a, b, c, d|
    printf('%d %d %d %d\n', a, b, c, d)
}
f() {|block|}

f() {|a, b, c, d|
    printf('%d %d %d %d\n', a, b, c, d)
}

ブロックは通常、関数オブジェクトとして扱われますが、シンボルの先頭にバッククオートをつけると quoted value として渡されます。これと、先に示した quoted value をブロック本体として渡す機能を使うと、ブロックの delegation ができます。

f(x:number) {`block} = {
    repeat(x) {|block|}
}