ブロックコード ジェネレータでは、内部ブロックのコードを複数回参照しなければならない場合があります。
たとえば、リストの最後の要素を出力するブロックがある場合、リストコードに複数回アクセスする必要があります。
// Incorrect block-code generator.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
// listCode gets referenced twice.
return `print(${listCode}[${listCode}.length - 1]);\n`;
}
ただし、内部ブロックのコードの結果の値が一致しない場合や、副作用が生じる場合は、問題が発生する可能性があります。たとえば、内部コードが実際には関数呼び出しである場合、次の特定のコードは範囲外の状態になる可能性があります。
print(randomList()[randomList().length - 1]);
一時変数に代入すると、内部ブロックのコードを 1 回だけ評価できます。
一時変数
一時変数には内部ブロックのコード文字列の値が格納されます。コードが評価されるのは 1 回だけで、その後その値は複数回参照できます。
import {javascriptGenerator, Order} from 'blockly/javascript';
// Correct block-code generator.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
const listVar = generator.nameDB_.getDistinctName(
'temp_list', Blockly.names.NameType.VARIABLE);
// listCode only gets evaluated once.
const code = `var ${listVar} = ${listCode};\n`;
return `print(${listVar}[${listVar}.length - 1]);\n`;
}
getDistinctName
呼び出しは、必要な変数名を受け取り、ユーザー定義変数と競合しない名前を返します。
冗長なコードの削減
一時変数の欠点は、内部ブロックのコードが関数や式ではなく値の場合、コードが冗長になることです。
// Assigning to temp_list is unnecessary.
var temp_list = foo;
print(temp_list[temp_list.length - 1]);
すっきりしたコードを生成するには、内部ブロックのコードが値かどうかを確認し、値でない場合は一時変数のみを含めます。
if (listCode.match(/^\w+$/)) {
const code = `print(${listCode}[${listCode}.length - 1]);\n`;
} else {
const listVar = generator.nameDB_.getDistinctName(
'temp_list', Blockly.names.NameType.VARIABLE);
const code = `var ${listVar} = ${listCode};\n`;
code += `print(${listVar}[${listVar}.length - 1]);\n`;
}