Globale o locale?

Jake 💥 rockz e oggi ci racconta di un caso che neanche Dirk Gentlyquesto.

Lo riscrivo con una piccola modifica (l’istruzione print per vedere il risultato), file e0.py:

def g(x):
  def f():
    x += 1
  f()

print(g(0))

Err! 😡, e come dice Jake: That was a fun one to explain 😀. Dico subito che da solo non ci sono arrivato; poi mi sono detto che dovevo pensarci ma…

Provo a fare come avrei dovuto. Invece di operare sulla variabile (globale) x nella f() ritorno il valore di x modificato, così (file e1.py):

def g(x):
  def f():
    return x + 1
  f()

print(g(0))

OOPS! per Python è OK ma non per me, correggo (e2.py):

def g(x):
  def f():
    return x + 1
  return f()

print(g(0))

Chiaro? Torno al tweet, nel thread c’è la dritta, il link a –indovina– la documentazione ufficiale di Python, qui: Why am I getting an UnboundLocalError when the variable has a value?

La spiegazione è diversa (più esaustiva) della mia, in sintesi:

This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value to x, the compiler recognizes it as a local variable. Consequently when the earlier print(x) attempts to print the uninitialized local variable and an error results.

Logico. E con certi linguaggi –nuovi, di moda– le variabili, quando ci sono, sono quelle, restano tali, niente modifiche. Per esempio Haskell –lasciato come esercizio. È da tanto che volevo dire questa frase 😊.

Invece in un’altra famiglia di linguaggi –sexy, i miei preferiti– non serve scrivere “return“, la funzione è un valore (file e3.rkt):

(define (g x)
  (define (f)
    (+ 1 x))
    (f))

(displayln (g 0))

Ma nessun lisper userebbe la funzione f(), viene chiamata una volta sola, per questo c’è lambda (e4.rkt):

(define (g x)
  ((lambda (t) (+ 1 t)) x))

(displayln (g 0))

Non mi piace, inutilmente contorta, basta (e5.rkt):

(define (g x)
  (+ 1 x))

(displayln (g 0))

Dove ho semplificato (troppo? f e g in un programma reale conterranno altre istruzioni) il codice. Ma era ridondante, lambda si usa quando serve.

Poi ci ho ripensato ancora (tutta la notte) e sono giunto a una conclusione che forse vale solo per me ma penso che il codice del tweet sia sbagliato come progettazione. Se x dev’essere globale si deve dichiarare, così (e6.py):

def g(x):
  def f():
    global x
    x += 1
  f()

# main
x = 0
g(0)
print(x)

Ovviamente il parametro in g() è inutile e dannoso come si scoprirà nel debug della prima revisione (e7.py):

def g(x):
  def f():
    global x
    x += 1
  f()

# main
x = 0
g(42) # <- ====
print(x)

Posta un commento o usa questo indirizzo per il trackback.

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: