Scopo delle variabili
Iniziamo con questo articolo una serie di post sul generatore di codice Blockly. Vogliamo fornire informazioni su come usare al meglio questo ambiente di apprendimento a blocchi, condividendo le esperienze fatte in corso d’opera.
In questo articolo affrontiamo un problema abbastanza frequente che provoca errori difficili da scoprire e correggere per un principiante: le variabili in Blockly sono sempre modificabili, dentro e fuori dalle funzioni!
Caratteristica un po’ scomoda, soprattutto se si struttura il codice con molte funzioni. La soluzione è quella di accertarsi che una funzione che ne chiama altre abbia variabili con nomi diversi da quelli usati nelle funzioni chiamate!
Scopo delle variabili
Lo scopo di una variabile, in un linguaggio di programmazione, è l’ambiente in cui quella variabile è conosciuta e può essere usata. In genere questo ambiente (a volte anche definito anche spazio dei nomi) coincide con il blocco di codice in cui viene dichiarata.
Per cui se definiamo una variabile all’interno di una funzione o di una procedura, ci aspettiamo
- che questa non sia modificabile all’esterno della procedura stessa;
- che altre variabili con lo stesso nome, definite e modificate in altre funzioni, non abbiano nessun effetto sulla nostra variabile, malgrado il nome sia lo stesso.
In altre parole, una variabile può essere usata solo all’interno dello spazio dei nomi in cui è definita! Per esempio, prendiamo il seguente codice scritto in Python (ricordiamo che Python non richiede una dichiarazione esplicita delle variabili, una variabile è definita non appena le viene assegnato un valore):
def UnaFunzione():
a = 1
print('sono dentro la funzione, il valore di a: %d' % a)
a = 37
UnaFunzione()
print('sono fuori dalla funzione, il valore di a: %d' % a)
Il codice definisce una funzione che, con molta fantasia, si chiama UnaFunzione, che ha il solo scopo di assegnare un valore (e quindi dichiarare) la variabile a e successivamente stampare il valore di questa variabile (che poi è pari a uno). Il programma vero e proprio è composto di sole tre righe che assegnano un valore alla variabile a, chiamano la funzione UnaFunzione e stampano il valore della variabile a. L’output del programma è il seguente:
sono dentro la funzione, il valore di a: 1
sono fuori dalla funzione, il valore di a: 37
Si vede chiaramente che le due variabili, pur avendo lo stesso nome, hanno scopo differente e quindi sono diverse. La variabile a che è stata dichiarata all’interno della funzione ha valore uno, quella dichiarata nel programma principale ha valore 37. Si tratta di due variabili differenti!
Blockly e le variabili
In Blockly le variabili sono dichiarate e inserite nell’insieme di blocchi chiamato Variables e sono note (e possono essere modificate) in qualunque blocco sul piano di lavoro, sia esso un ciclo o una funzione. Come esempio prendiamo i blocchi seguenti:
Ci sono due gruppi di blocchi:
- il primo, il più semplice, richiede un numero all’utente (contenuto nella variabile x) e poi calcola il fattoriale dei numeri naturali compresi tra uno e x (notare il ciclo count with in colore verde che usa la variabile i per contare da uno a x);
- il secondo gruppo definisce una funzione – Fattoriale – che calcola effettivamente il fattoriale di un numero (anche qui abbiamo un ciclo count with in colore verde che usa la variabile i per contare da uno a x).
Ogni blocco usa un ciclo che itera sulla variabile i. Così come è strutturato il programma non funziona, non fornisce i risultati sperati. Infatti, avviamo il programma e inseriamo il numero 4, l’output è il seguente:
1
2
24
mentre avrebbe dovuto essere:
1
2
6
24
Purtroppo, la variabile che è stata usata nei due cicli – i – è la stessa e la funzione Fattoriale la modifica ogni volta che viene chiamata ma la cosa più grave è che la modifica proprio dentro il ciclo del blocco principale producendo risultati imprevedibili.
Basta una piccola correzione per ottenere un programma che funziona correttamente: cambiare la variabile su cui opera uno dei due cicli! Nei blocchi che seguono il ciclo contenuto nella funzione Fattoriale ora lavora sulla variabile j (evidenziata dagli ovali in colore rosso) mentre quello contenuto nel blocco principale opera con la variabile i. In questo modo la funzione non modifica più il valore di i e i risultati sono quelli attesi.
Infatti, inserendo il numero 4, come in precedenza, si ottiene come output:
1
2
6
24
proprio quello che ci saremmo aspettati.
Il codice in Python
Qui di seguito mostriamo le linee essenziali della traduzione in Python dei blocchi che abbiamo analizzato (questa è la prima versione, quella non corretta):
x = None
n = None
i = None
fattoriale = None
def text_prompt(msg):
...
...
def Fattoriale(n):
global x, i, fattoriale
...
return fattoriale
Si notano immediatamente due cose:
- alle variabili, a tutte le variabili, anche a quelle usate solo all’interno della funzione Fattoriale, è assegnato un valore (none) immediatamente e quindi appartengono allo spazio dei nomi del programma principale (le righe di codice corrispondenti sono evidenziate in grassetto);
- le variabili son tutte dichiarate global all’interno della funzione Fattoriale e quindi sono le stesse, esattamente le stesse, dello spazio dei nomi del programma principale (le righe corrispondenti sono anche in questo caso evidenziate in grassetto).
Il codice dell’esempio
Qui di seguito il codice XML generato da Blockly per i blocchi che abbiamo usato in questo articolo (versione corretta). Si possono copiare e incollare nella Tab XML dell’ambiente Blockly che trovate su questo blog, basta seguire questo collegamento.
<xml xmlns="http://www.w3.org/1999/xhtml">
<variables>
<variable type="" id="H0oeZm~i_h[4o1hXABHE">x</variable>
<variable type="" id="K0[-4Lz0DD+G,%QO8@lf">n</variable>
<variable type="" id="BQ!n4)hPA57[:)x6M5|[">i</variable>
<variable type="" id="N$(jJBqN30h9pPvZb7Qq">fattoriale</variable>
<variable type="" id="@X!n6/wZ@OHj#Rdqq=lO">j</variable>
</variables>
<block type="variables_set" id="Akx_Cem*!5*KlrLGd{+K" x="-963" y="-262">
<field name="VAR" id="H0oeZm~i_h[4o1hXABHE" variabletype="">x</field>
<value name="VALUE">
<block type="text_prompt_ext" id="XD`6);_A]h=s83IMo-fT">
<mutation type="NUMBER"></mutation>
<field name="TYPE">NUMBER</field>
<value name="TEXT">
<shadow type="text" id="EeP!LrTNUuXGG1~@@m81">
<field name="TEXT">Inserisci un numero intero ...</field>
</shadow>
</value>
</block>
</value>
<next>
<block type="controls_for" id="8DdxdJ_vAAlh$R`{@2H6">
<field name="VAR" id="BQ!n4)hPA57[:)x6M5|[" variabletype="">i</field>
<value name="FROM">
<shadow type="math_number" id="lD8pqRH7^%:?zsE-k!vn">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number" id="F3%v53!%a`^$Qyiy-V6e">
<field name="NUM">10</field>
</shadow>
<block type="variables_get" id=")J)8X@|,VA(#+Og./m.e">
<field name="VAR" id="H0oeZm~i_h[4o1hXABHE" variabletype="">x</field>
</block>
</value>
<value name="BY">
<shadow type="math_number" id="b5)SXvE5K;FH@9GNPBV-">
<field name="NUM">1</field>
</shadow>
</value>
<statement name="DO">
<block type="text_print" id="HMo8?)fxfRS1Nap%8F~h">
<value name="TEXT">
<shadow type="text" id="5ihGKl%p:?V;1e!cg,WL">
<field name="TEXT">abc</field>
</shadow>
<block type="procedures_callreturn" id="s%)j4kS3V1@K!u%J2tm5" inline="true">
<mutation name="Fattoriale">
<arg name="n"></arg>
</mutation>
<value name="ARG0">
<block type="variables_get" id="4,OnhdvU|I}yS8Q0Aj)]">
<field name="VAR" id="BQ!n4)hPA57[:)x6M5|[" variabletype="">i</field>
</block>
</value>
</block>
</value>
</block>
</statement>
</block>
</next>
</block>
<block type="procedures_defreturn" id="1^C?`2F-~|u|wH|Q%N9k" x="-963" y="-113">
<mutation>
<arg name="n" varid="K0[-4Lz0DD+G,%QO8@lf"></arg>
</mutation>
<field name="NAME">Fattoriale</field>
<comment pinned="false" h="194" w="412">Calcola il fattoriale del numero n come prodotto di 2 * ... * n, n deve essere un numero naturale.
Parametri:
n: il numero di cui calcolare il fattoriale
Valore restituito:
n! (il fattoriale di n) se n è un numero naturale, infinito in caso contrario
</comment>
<statement name="STACK">
<block type="procedures_ifreturn" id="e@SA?y-U3NAp60h;NuIn">
<mutation value="1"></mutation>
<value name="CONDITION">
<block type="logic_negate" id="B9YpLBD6(uTKfEaZBaX@">
<value name="BOOL">
<block type="math_number_property" id="dB[t6})/3@9NaJ4zZ`0g">
<mutation divisor_input="false"></mutation>
<field name="PROPERTY">WHOLE</field>
<value name="NUMBER_TO_CHECK">
<shadow type="math_number" id="[?|hR[:$Zgej*mjTDF}J">
<field name="NUM">0</field>
</shadow>
<block type="variables_get" id="N+61Gve!l#dkS;3(p6y/">
<field name="VAR" id="K0[-4Lz0DD+G,%QO8@lf" variabletype="">n</field>
</block>
</value>
</block>
</value>
</block>
</value>
<value name="VALUE">
<block type="math_constant" id="e6-%A62=JLQQ4Ljyo+6v">
<field name="CONSTANT">INFINITY</field>
</block>
</value>
<next>
<block type="procedures_ifreturn" id="`YT~M=(L:Ma58J}dt??_">
<mutation value="1"></mutation>
<value name="CONDITION">
<block type="logic_operation" id="eVTphM=m0OoE^w?t6NPH">
<field name="OP">OR</field>
<value name="A">
<block type="logic_compare" id="22[w.Z-7$}Kt|l021$@3">
<field name="OP">EQ</field>
<value name="A">
<block type="variables_get" id=",tBdB7;)CPWzUHE/Wl*l">
<field name="VAR" id="K0[-4Lz0DD+G,%QO8@lf" variabletype="">n</field>
</block>
</value>
<value name="B">
<block type="math_number" id="aMD[;s]`5$AK7d)7ub,=">
<field name="NUM">0</field>
</block>
</value>
</block>
</value>
<value name="B">
<block type="logic_compare" id="h|i61Cve;g|^Sp%/)p#~">
<field name="OP">EQ</field>
<value name="A">
<block type="variables_get" id="9cv=TV-1IlnI7MH+BrHN">
<field name="VAR" id="K0[-4Lz0DD+G,%QO8@lf" variabletype="">n</field>
</block>
</value>
<value name="B">
<block type="math_number" id="jVv$D3Fo_`jH3we9ak6}">
<field name="NUM">1</field>
</block>
</value>
</block>
</value>
</block>
</value>
<value name="VALUE">
<block type="math_number" id="*V)gZZ)T_vXdrSd]qOa*">
<field name="NUM">1</field>
</block>
</value>
<next>
<block type="procedures_ifreturn" id="F}l5,;@x$%n5s,~FS)A/">
<mutation value="1"></mutation>
<value name="CONDITION">
<block type="logic_compare" id="UR!UxOkpql%glnPo-^..">
<field name="OP">LT</field>
<value name="A">
<block type="variables_get" id="y!yS%wZk=_5ZCHOJ@hA,">
<field name="VAR" id="K0[-4Lz0DD+G,%QO8@lf" variabletype="">n</field>
</block>
</value>
<value name="B">
<block type="math_number" id="J]bzt4a#Guzs(H_{y{DS">
<field name="NUM">0</field>
</block>
</value>
</block>
</value>
<value name="VALUE">
<block type="math_constant" id="%M%!(hu@Xo15;L.^7y=f">
<field name="CONSTANT">INFINITY</field>
</block>
</value>
<next>
<block type="variables_set" id=".+sW`nFpdR*[(a}O5OGa">
<field name="VAR" id="N$(jJBqN30h9pPvZb7Qq" variabletype="">fattoriale</field>
<value name="VALUE">
<block type="math_number" id="yrBI$-wRFbadStbSEifz">
<field name="NUM">1</field>
</block>
</value>
<next>
<block type="controls_for" id="f^!or}rJmr?(@6U`tsG1">
<field name="VAR" id="@X!n6/wZ@OHj#Rdqq=lO" variabletype="">j</field>
<value name="FROM">
<shadow type="math_number" id=".Tf-qgx@N-_/zwdkjR4n">
<field name="NUM">2</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number" id="6J[p5_:N`(/M%21d#W?I">
<field name="NUM">10</field>
</shadow>
<block type="variables_get" id="VLtV:S2TuoFyH%,0!]X:">
<field name="VAR" id="K0[-4Lz0DD+G,%QO8@lf" variabletype="">n</field>
</block>
</value>
<value name="BY">
<shadow type="math_number" id="S8+9P)J[,;X~j/}sj3|1">
<field name="NUM">1</field>
</shadow>
</value>
<statement name="DO">
<block type="variables_set" id="Xf4GM.2aWUioacIkpmfO">
<field name="VAR" id="N$(jJBqN30h9pPvZb7Qq" variabletype="">fattoriale</field>
<value name="VALUE">
<block type="math_arithmetic" id="^l2R8cyy/-5;C}FnvBzZ">
<field name="OP">MULTIPLY</field>
<value name="A">
<shadow type="math_number" id="C4[6;I*q#UooBsJn+`e8">
<field name="NUM">1</field>
</shadow>
<block type="variables_get" id="K].?qD[CC;^~Y-zNk.E+">
<field name="VAR" id="N$(jJBqN30h9pPvZb7Qq" variabletype="">fattoriale</field>
</block>
</value>
<value name="B">
<shadow type="math_number" id="@q!(0k38~mYG^:7{STgO">
<field name="NUM">1</field>
</shadow>
<block type="variables_get" id="xFV2]fyu9IbEqlbGpYAl">
<field name="VAR" id="@X!n6/wZ@OHj#Rdqq=lO" variabletype="">j</field>
</block>
</value>
</block>
</value>
</block>
</statement>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</next>
</block>
</statement>
<value name="RETURN">
<block type="variables_get" id="]U)73Qlh6|LO0~nL+`3+">
<field name="VAR" id="N$(jJBqN30h9pPvZb7Qq" variabletype="">fattoriale</field>
</block>
</value>
</block>
</xml>