Kode blok nilai dalam cache

Blok nilai sesuai dengan ekspresi. Saat menggunakan blok nilai sebagai blok dalam, Anda mungkin perlu menggunakan ekspresi yang dihasilkan lebih dari sekali dalam kode blok. Misalnya, blok yang mendapatkan elemen terakhir dari daftar menggunakan ekspresi yang membuat daftar dua kali.

// Incorrect block-code generator.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
  // Get the expression that creates the list.
  const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);

  // listCode is used twice.
  const code = `${listCode}[${listCode}.length - 1]`;

  return [code, Order.MEMBER];
}

Hal ini menyebabkan masalah jika kode blok dalam menghasilkan nilai yang berbeda setiap kali dieksekusi, atau jika kode memiliki efek samping. Misalnya, jika kode blok dalam sebenarnya adalah panggilan fungsi, kode khusus ini dapat menyebabkan kondisi di luar rentang:

randomList()[randomList().length - 1]

Untuk menghindari masalah ini, kode Anda harus menjalankan kode blok dalam tepat sekali. Ada 2 cara untuk melakukannya:

  • Variabel sementara: Menyimpan hasil evaluasi kode blok dalam dalam variabel sementara dan menggunakan variabel sementara. Anda hanya dapat menggunakan metode ini jika blok Anda adalah blok pernyataan.

  • Fungsi utilitas: Buat fungsi yang melakukan tugas yang perlu Anda lakukan dan teruskan hasil evaluasi kode blok dalam sebagai argumen ke fungsi ini. Anda dapat menggunakan metode ini untuk blok nilai dan pernyataan.

Variabel sementara

Variabel sementara menyimpan nilai string kode blok dalam sehingga kode hanya dievaluasi satu kali, lalu nilai dapat direferensikan beberapa kali.

Variabel sementara tidak dapat digunakan dalam blok nilai karena blok nilai harus menampilkan satu baris kode. Sebagai gantinya, gunakan fungsi utilitas.

import {javascriptGenerator, Order} from 'blockly/javascript';

// Correct block-code generator for a statement block that prints the last element of a list.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
  // Get the expression that creates the list.
  const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
  // Get the name of a temporary variable.
  const listVar = generator.nameDB_.getDistinctName(
      'temp_list', Blockly.names.NameType.VARIABLE);

  // Evaluate listCode once and assign the result to the temporary variable.
  const code = `var ${listVar} = ${listCode};\n`;
  // Print the last element of the list.
  code += `print(${listVar}[${listVar}.length - 1]);\n`;
  return code;
}

Misalnya, jika kode blok dalam adalah panggilan fungsi randomList(), kode yang dihasilkan adalah:

var temp_list = randomList();
print(temp_list[temp_list.length - 1]);

Panggilan getDistinctName menggunakan nama variabel yang Anda inginkan, dan menampilkan nama yang tidak bertentangan dengan variabel buatan pengguna.

Mengurangi kode duplikat

Kelemahan variabel sementara adalah jika kode blok dalam adalah nilai, bukan fungsi atau ekspresi, Anda akan mendapatkan kode yang redundan:

// Assigning to temp_list is unnecessary.
var temp_list = foo;
print(temp_list[temp_list.length - 1]);

Untuk menghasilkan kode yang lebih rapi, Anda dapat memeriksa apakah kode blok dalam adalah nilai, dan hanya menyertakan variabel sementara jika bukan.

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`;
}

Fungsi utilitas

Fungsi utilitas adalah fungsi yang ditentukan developer yang disertakan sebagai bagian dari string kode yang dihasilkan. Anda dapat menggunakannya untuk memastikan bahwa kode blok dalam hanya dievaluasi satu kali, lalu nilainya dapat direferensikan beberapa kali.

Fungsi utilitas dapat digunakan dalam blok nilai dan blok pernyataan. Namun, blok pernyataan umumnya harus menggunakan variabel sementara, yang biasanya lebih mudah dibaca.

import {javascriptGenerator, Order} from 'blockly/javascript';

// Correct block-code generator for a value block that gets the last element of a list.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
  // Get the expression that creates the list.
  const listCode = generator.valueToCode(block, 'LIST', Order.NONE);
  // Create a function that accepts a list and returns its last element. The
  // language generator adds this function to your code.
  const functionName = generator.provideFunction_(
      'list_lastElement',
      [
        `function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list) {`,
        `  return list[list.length - 1];`,
        `}`
      ]
  );

  // Create an expression that calls the function with listCode as its argument.
  // This evaluates listCode once and passes the resulting list to the function.
  const code = `${functionName}(${listCode})`;
  return [code, Order.FUNCTION_CALL];
}

Misalnya, jika kode blok dalam adalah panggilan fungsi randomList(), kode yang dihasilkan adalah:

// This code is added to the overall code returned by the code generator.
function list_lastElement(list) {
  return list[list.length - 1];
}

// This code is returned by your inner block.
list_lastElement(randomList());

Memberikan fungsi

Anda dapat menentukan fungsi utilitas di dalam generator kode blok menggunakan provideFunction_. Fungsi ini menggunakan nama yang Anda inginkan untuk fungsi utilitas, dan array string kode yang berisi definisi fungsi. Fungsi ini menampilkan nama yang dihasilkan dari fungsi utilitas Anda, setelah (mungkin) mengubahnya agar tidak bertentangan dengan fungsi yang ditentukan pengguna.

provideFunction_ juga menghapus duplikat definisi fungsi utilitas, sehingga setiap fungsi utilitas hanya ada satu kali, meskipun jenis blok yang menentukannya ada beberapa kali.

Memperbarui prioritas

Saat menentukan fungsi utilitas, Anda juga harus memperbarui prioritas (yang menentukan cara tanda kurung disisipkan) yang disertakan dalam generator kode blok.

Prioritas selalu didasarkan pada string kode yang ditampilkan oleh generator kode blok. Operator di dalam fungsi utilitas tidak akan dipedulikan. Jadi, dalam contoh sebelumnya, panggilan valueToCode diubah menjadi Order.NONE dan tuple yang ditampilkan diubah menjadi Order.FUNCTION_CALL.