8.データ結合
bind鍵語は、対象となる変数の値を別の変数の値に結びつける。
let x = bind someExpression;
これは、someExpressionの結果を変数xに結びつける。someExpressionの値が変わると、xの値も自動的に更新される。結び付けられる変数は、ある基本データ型の単純な値、式の結果、区画式の結果または結合関数でもよい(let鍵語は、それらに割り当てることができない変数を導入する)。
8.1 再計算して更新する
ある状況では、更新がどのように行われるか、あるいは結び付けられる値が変化するということはどういう意味なのかを厳密に知っている必要ある。結び付けられる値が変化すると、最小限の再計算が実行される。このことは、ある限られた状況において重要となる。たとえば、オブジェクトの生成を必要とする結合が実行される場合で、オブジェクトの同一性の理由から、新たなオブジェクトが実際に生成されるか否かが問題となるような状況である。
let sum = bind expr1 + expr2;
(上記の例で、)expr2が変化するとsumは再計算されるが、expr1は再計算されない。expr1に対する値はすでに格納されており、単にその値を再度取ってくるだけである。
次に実例を示す。
import java.lang.System;
var y = 3;
function ten() : Integer { 10 }
let sum = bind ten() + y;
System.out.println(sum);
y = 7;
System.out.println(sum);
このプログラムの出力は次のようになる。
13
17
yが7に設定されたとき、関数ten()は変化していないので再度呼び出されることはない。関数値は記憶されており再利用される。
8.2 結合と区画式
結合において、区画式の中に存在しうるのは変数宣言文だけである。(1増および1減を含め)値の割り当ては禁止されていることに留意せよ。したがって、結合区画式は次のような形式を取る。
bind { var a = expr; var b = expr; var c = expr; expr }
結合された式に対するいかなる変化も更新を引き起こし、その更新は最小限なので、変数が有効的に結合されていることを確認するのは容易である。while、insertおよびdeleteは文なので、結合の中に現れてはならないことに留意せよ。
8.3 結合と条件式
次の例を考えてみよ。
let x = bind if (condExpr) expr1 else expr2;
condExprが変わると、if文のどちらの分岐が評価されるのかも変わる。expr1またはexpr2に対する変化は、どちらも、もう一方の式の再計算を引き起こすことはない。
8.4 結合とfor式
次の例を考えてみよ。
let newSeq = bind for (elem in seq) expr;
たとえseqが変わっても、依然としてseqに存在し続けている要素に対応するnewSeqの要素は、再計算されない。言い換えれば、要素がseqに挿入されると、その要素にexprを適用した結果がnewSeqの相当する位置に挿入され、newSeqの他の要素は再計算されない。
例外は、exprがindexof elemを使用しているために、索引の値が変化した要素を更新する必要がある場合であるが、最小限の更新という既定には合致している。
import java.lang.System;
var min = 0;
var max = 3;
function square(x : Integer) : Integer { x*x }
let values = bind for (x in [min..max]) square(x);
System.out.println(values);
max = 5;
System.out.println(values);
min = 1;
System.out.println(values);
min = 0;
System.out.println(values);
この場合、次の再計算が実行される。
・0から3の2乗を計算する。
・4と5の2乗を計算する(maxが変わっても、0から3の2乗は再計算されない)。
・ほかの値は再計算せず、0の2乗を削除する。
・0の2条を戻す。これは、その値の再計算を必要とする。この動作は、代わりにinsertやdeleteを用いても同様である。
8.5 結合とオブジェクト直定数
オブジェクト直定数は、演算子や非結合関数のように機能する。オブジェクト直定数の引数の一つが変化すると、オブジェクト直定数は再実行(新たな実体が生成)される。
let pt = bind Point { x: myX y: myY }
myXが変わると、JavaFX台本型プログラミング言語は、新たなPointオブジェクトを生成する。これは、不変オブジェクトに対して予期された振る舞いでもある。
新たなオブジェクトを生成せずに、xの値がmyXの値を追跡するようにするためには、結合が必要になる。
let pt = bind Point { x: bind myX y: myY }
この例に対しては、おそらくyも同様に結合した方が良いだろう。
let pt = bind Point { x: bind myX y: bind myY }
ここで、ptは常に同じPointオブジェクトの実体と結合したままである。それは、冒頭のbindがなくても同様である。
let pt = Point { x: bind myX y: bind myY }
8.6 結合と関数
非結合関数は、bound鍵語が前置されていない関数である。Javaプログラミング言語の操作やJavaFX台本型プログラミング言語の関数の呼び出しに関して、その引数のいずれかが変更されると、JavaFX台本型プログラミング言語はその関数を再度呼び出すが、関数の本体は暗箱である。関数が、その入力となる引数以外に持っている依存性のゆえに再計算されることはない。
import java.lang.System;
class Point {
attribute x : Number;
attribute y : Number;
}
var scale = 1.0;
function makePoint(x0 : Number, y0 : Number) : Point {
Point {
x: x0 * scale
y: y0 * scale
}
}
var myX = 3.0;
var myY = 3.0;
let pt = bind makePoint(myX, myY);
System.out.println(pt.x);
myX = 10.0;
System.out.println(pt.x);
scale = 2.0;
System.out.println(pt.x);
出力。
3.0
10.0
10.0
引数myXを変更すると、関数makePointは再度呼び出される。しかし、関数makePointは暗箱である。よって、scaleに割り当てられた値が変更されても、当然ではあろうが、関数は再計算されない。これこそが、結合関数が役立つことを意図された場面である。結合関数が予期された更新をもたらしてくれる。
結合関数は、その本体として、結合される区画式を持っている(それゆえに、結合区画式に関する上述の制限を有している)。結合関数に結びつけると、引数以外の変化も更新を引き起こし、かつ引数の変更も調べられる。上述の関数makePointを結合関数にすると次のようになる。
bound function makePoint(x0 : Number, y0 : Number) : Point {
今度こそ、scaleに割り当てられた値の変化は更新(20.0)を引き起こす。myXが変わるとx0 * scaleだけが再計算され、y0 * scaleは再計算されないことに留意せよ。
結合とは別のところで結合関数を呼び出すことは、非結合関数を呼び出すことと同じことである。
0 件のコメント:
コメントを投稿