Cose da non fare mai: goto

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! ;-)

About these ads
Posta un commento o usa questo indirizzo per il trackback.

Commenti

  • Matteo Pani (@_mattux_)  Il 03/26/2012 alle 15:21

    Il goto è il male, io non l’ho mai usato (poi certo, in Assembly non potevo far altro che jumpare).
    Stackoverflow invece è il bene :D

    • juhan  Il 03/26/2012 alle 16:59

      Sì, non ho mai dubitato di te. Ma allora si usa ancora jumpare :-D

  • Giuseppe (@glipari)  Il 03/26/2012 alle 15:45

    Grandissimo post! ma dove li scovi? (goto in python, incredibile)

    • juhan  Il 03/26/2012 alle 16:58

      non c’è mai limite al peggio ;-)

  • evilripper  Il 04/10/2012 alle 12:52

    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. :-D
    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ù. :-)

Lascia un Commento

Fill in your details below or click an icon to log in:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. 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...

Iscriviti

Ricevi al tuo indirizzo email tutti i nuovi post del sito.

Unisciti agli altri 37 follower

%d bloggers like this: