Stack Overflow è una miniera inesauribile! Da cercarci le cose e consultare spesso, sempre. Però alle volte capita anche di dissentire, come in questo caso: `goto` in Python.
Ecco la richiesta di Albert ha avuto una risposta, soddisfacente, funziona. Ma se Albert lavorasse con me tenterei, con tutte le mie forze, di dissuaderlo. E se lavorasse per me chiamerei subito la ministra Fornero.
Intanto ecco il modulo goto.py di Carl Cerecke, l’ho modificato eliminando le linee dopo la numero 85.
## {{{ http://code.activestate.com/recipes/576944/ (r5)
import dis
import new
class MissingLabelError(Exception):
"""'goto' without matching 'label'."""
pass
def goto(fn):
"""
A function decorator to add the goto command for a function.
Specify labels like so:
label .foo
Goto labels like so:
goto .foo
"""
labels = {}
gotos = {}
globalName = None
index = 0
end = len(fn.func_code.co_code)
i = 0
# scan through the byte codes to find the labels and gotos
while i < end:
op = ord(fn.func_code.co_code[i])
i += 1
name = dis.opname[op]
if op > dis.HAVE_ARGUMENT:
b1 = ord(fn.func_code.co_code[i])
b2 = ord(fn.func_code.co_code[i+1])
num = b2 * 256 + b1
if name == 'LOAD_GLOBAL':
globalName = fn.func_code.co_names[num]
index = i - 1
i += 2
continue
if name == 'LOAD_ATTR':
if globalName == 'label':
labels[fn.func_code.co_names[num]] = index
elif globalName == 'goto':
gotos[fn.func_code.co_names[num]] = index
name = None
i += 2
# no-op the labels
ilist = list(fn.func_code.co_code)
for label,index in labels.items():
ilist[index:index+7] = [chr(dis.opmap['NOP'])]*7
# change gotos to jumps
for label,index in gotos.items():
if label not in labels:
raise MissingLabelError("Missing label: %s"%label)
target = labels[label] + 7 # skip NOPs
ilist[index] = chr(dis.opmap['JUMP_ABSOLUTE'])
ilist[index + 1] = chr(target & 255)
ilist[index + 2] = chr(target >> 8)
# create new function from existing function
c = fn.func_code
newcode = new.code(c.co_argcount,
c.co_nlocals,
c.co_stacksize,
c.co_flags,
''.join(ilist),
c.co_consts,
c.co_names,
c.co_varnames,
c.co_filename,
c.co_name,
c.co_firstlineno,
c.co_lnotab)
newfn = new.function(newcode,fn.func_globals)
return newfn
E questo è un esempio minimo d’uso, preso sempre da Carl
from goto import goto
@goto
def test1(n):
s = 0
label .myLoop
if n <= 0:
return s
s += n
n -= 1
goto .myLoop
print test1(10)
Semplice e chiarissimo vero? La funzione test1(n) restituisce la somma dei numeri interi tra 1 e n. Cosa che si potrebbe fare prima di subito con un ciclo for e ancora prima con n * (1 + n) / 2.
Se il ciclo fosse indefinito (while) già avrebbe più senso per quei linguaggi che non hanno quell’istruzione. Qualcuno ha detto Fortran di una volta? Esatto e assembler vari. A me è capitato di trovare cose di questo tipo, anche i linguaggi di programmazione sono soggetti all’evoluzione. In Fortran il GOTO era indispensabile, anche mascherato come IF. Nei tempi antichi esisteva il verbo jumpare (credo oggi sia obsoleto). Ma con Python, ragazzi, io dico di no!
Poi possono capitare casi più rognosi, come quello di dover uscire da un ciclo profondamente annidato, come qui
from goto import goto
@goto
def test():
for i in range(1, 10):
for j in range(1, 20):
for k in range(1, 30):
print i, j, k
if k == 3:
goto .end
label .end
#main
test()
Ma, ragazzi, se io vedo una cosa così faccio la spia a Dijkstra avvisati, nèh!


Commenti
Il goto è il male, io non l’ho mai usato (poi certo, in Assembly non potevo far altro che jumpare).
Stackoverflow invece è il bene
Sì, non ho mai dubitato di te. Ma allora si usa ancora jumpare
Grandissimo post! ma dove li scovi? (goto in python, incredibile)
non c’è mai limite al peggio
uhahaha goto genera spesso “coding horror…” tuttavia il codice orrido non è mai causato dagli strumenti ma dagli sviluppatori e non escludo ( naturalmente in linguaggi a basso livello) che in qualche caso è possibile che possa anche servire un goto.
Nella maggioranza dei casi il goto è stato rimosso proprio per impedire ai programmatori di usarlo un pò come tagliare le mani a un ladro affinchè non rubi più.