I blocchi di valori corrispondono alle espressioni. Quando utilizzi un blocco di valore come blocco interno, potresti dover utilizzare l'espressione generata più di una volta nel codice del blocco. Ad esempio, un blocco che recupera l'ultimo elemento di un elenco utilizza l'espressione che crea l'elenco due volte.
// 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];
}
Ciò causa problemi se il codice del blocco interno genera valori diversi ogni volta che viene eseguito o se il codice ha effetti collaterali. Ad esempio, se il codice del blocco interno è in realtà una chiamata di funzione, questo codice specifico può causare una condizione fuori intervallo:
randomList()[randomList().length - 1]
Per evitare questo problema, il codice deve eseguire il codice di un blocco interno esattamente una volta. Puoi utilizzare due metodi:
Variabili temporanee: memorizza nella cache il risultato della valutazione del codice del blocco interno in una variabile temporanea e utilizza la variabile temporanea. Puoi utilizzare questo metodo solo se il blocco è un blocco di istruzioni.
Funzioni di utilità: crea una funzione che esegua il lavoro da svolgere e passa il risultato della valutazione del codice del blocco interno come argomento a questa funzione. Puoi utilizzare questo metodo sia per i blocchi di valore che per quelli di istruzioni.
Variabili temporanee
Una variabile temporanea memorizza il valore della stringa di codice di un blocco interno in modo che il codice venga valutato una sola volta e poi il valore possa essere fatto riferimento più volte.
Le variabili temporanee non possono essere utilizzate nei blocchi di valori perché devono restituire una singola riga di codice. Utilizza invece una funzione di utilità.
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;
}
Ad esempio, se il codice del blocco interno è la chiamata di funzione randomList()
, il codice generato è:
var temp_list = randomList();
print(temp_list[temp_list.length - 1]);
La chiamata getDistinctName
prende il nome della variabile che ti interessa e restituisce un nome che non è in conflitto con le variabili definite dall'utente.
Riduci il codice ridondante
Lo svantaggio delle variabili temporanee è che, se il codice del blocco interno è un valore e non una funzione o un'espressione, ottieni un codice ridondante:
// Assigning to temp_list is unnecessary.
var temp_list = foo;
print(temp_list[temp_list.length - 1]);
Per produrre un codice più pulito, puoi verificare se il codice del blocco interno è un valore e includere la variabile temporanea solo se non lo è.
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`;
}
Funzioni di utilità
Una funzione di utilità è una funzione definita dallo sviluppatore inclusa nella stringa di codice generata. Puoi utilizzarli per assicurarti che il codice del blocco interno venga valutato una sola volta e che il valore possa essere fatto riferimento più volte.
Le funzioni di utilità possono essere utilizzate nei blocchi di valori e nei blocchi di istruzioni. Tuttavia, in genere i blocchi di istruzioni dovrebbero utilizzare variabili temporary, che sono in genere più leggibili.
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];
}
Ad esempio, se il codice del blocco interno è la chiamata di funzione randomList()
, il codice generato è:
// 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());
Fornisci la funzione
Puoi definire funzioni di utilità all'interno dei generatori di codice in blocco utilizzando
provideFunction_
. Accetta il nome che vuoi assegnare alla funzione di utilità e un array di stringhe di codice che contengono la definizione della funzione. Restituisce il nome risultante della funzione di utilità, dopo (eventualmente) averlo modificato in modo che non entri in conflitto con le funzioni definite dall'utente.
provideFunction_
elimina anche le duplicazioni delle definizioni delle funzioni di utilità, in modo che ogni funzione di utilità esista una sola volta, anche se il tipo di blocco che la definisce esiste più volte.
Aggiorna le precedenze
Quando definisci una funzione di utilità, devi anche aggiornare le precedenze (che definiscono come vengono inserite le parentesi) incluse nel generatore di codice a blocchi.
La precedenza si basa sempre sulla stringa di codice restituita dal generatore di codice
blocco. Non si preoccupa degli operatori all'interno delle funzioni di utilità. Pertanto, nell'esempio precedente la chiamata valueToCode
è stata modificata in Order.NONE
e la tuple di ritorno in Order.FUNCTION_CALL
.