"goto" w Pythonie

Muszę używać goto w Pythonie. I found entrians goto ale moja implementacja Pythona (CPython 2.7.1 na Mac) nie ma tego modułu, więc nie wydaje się być przenośna. Powinien on działać przynajmniej we wszystkich implementacjach Pythona, które obsługują kod bajtowy CPython (esp. Zależy mi na Cpythonie i PyPy). Następnie jest to powiązane pytanie i cdjc ' S goto. I te podane przez Odpowiedzi poniżej.

Mógłbym pójść i zbudować bajt ręcznie (tzn. napisać własny Kompilator Pythona), ponieważ istnieje taka instrukcja (JUMP_ABSOLUTE i przyjaciele). Ale zastanawiam się, czy jest łatwiejszy sposób. Czy za pomocą inspect można wywołać pojedynczą instrukcję kodu bajtowego? Myślałem też o kompilacji przez Pythona, a następnie automatycznym łataniu wygenerowanego bajtowego kodu Pythona.


Oczywiście ludzie będą pytać dlaczego i nie dadzą mi żadnej pomocnej odpowiedzi, jeśli nie wyjaśnię, dlaczego naprawdę tego potrzebuję. Tak w skrócie mój przypadek użycia: tłumaczę C AST Na Python AST i zestawiam to. Mogę odwzorować każdy logiczny przepływ (wszystkie pętle i inne rzeczy) w jakiś sposób do równoważnego kodu Pythona. Wszystko oprócz goto. Podobne projekty: PyCParser (Zobacz interpreter.py), PyCPython, PyLua .

Author: Albert, 2011-08-05

6 answers

Wiem o czym wszyscy myślą:

XKCD GOTO

Jednak mogą być pewne przypadki dydaktyczne, w których naprawdę potrzebujesz goto.

Ten przepis w Pythonie dostarcza polecenia goto jako dekoratora funkcji.

Dekorator goto (Python recipe by Carl Cerecke )

To jest przepis dla Ciebie, jeśli masz dość powolnej prędkości istniejący goto moduł http://entrian.com/goto/ . The goto w tym przepis jest o 60x szybszy i jest również czystszy (nadużywanie sys.settrace wydaje się raczej pythoniczny). Ponieważ jest to dekorator, ostrzega reader, które funkcje używają goto. Nie realizuje comefrom polecenia, choć nie jest trudno go w tym celu rozszerzyć (ćwiczenie dla czytelnika). Ponadto obliczone Goto nie są obsługiwane; nie są pythonic.

  • użyj dis.dis(fn), aby pokazać dezasemblację kodu bajtowego funkcji.
  • bajtowe kody funkcji to accessed by fn.func_code.co_code. To jest readonly tak:
  • dekorowana funkcja jest tworzona dokładnie tak samo jak stara, ale z kodem bajtowym zaktualizowanym tak, aby spełniał polecenia goto.
  • tu 2.tylko x; nowy moduł nie jest w Pythonie 3.x (inny ćwiczenia dla czytelnika!)

Użycie

@goto
def test1(n):
    s = 0

    label .myLoop

    if n <= 0:
        return s
    s += n
    n -= 1

    goto .myLoop

>>> test1(10)
55

Update

Oto dwie dodatkowe implementacje kompatybilne z Pythonem 3:

 43
Author: Paolo Moretti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-12 12:49:04

Możesz mieć jedyny prawidłowy przypadek użycia, jaki kiedykolwiek widziałem dla potrzeby goto w Pythonie. :-)

Najprostszym sposobem emulacji forward goto w Pythonie jest użycie wyjątków, ponieważ mogą one wyskakiwać z dowolnej głębokości zagnieżdżonych struktur kontrolnych.

class Goto(Exception):
    pass

try:
    if foo = "bar":
        raise Goto
    print "foo is not bar"
except Goto:
    print "foo is bar"

Robi się to kłopotliwe, jeśli potrzebujesz obsługiwać więcej niż jedno miejsce docelowe, ale myślę, że można to zrobić za pomocą zagnieżdżonych struktur try/except i wielu klas WYJĄTKÓW, po jednym dla każdego miejsca docelowego. Od C goto do zakres pojedynczej funkcji, przynajmniej nie będziesz musiał martwić się o to, jak to działa przez funkcje. :- ) Oczywiście nie działa na odwrotne gotos.

Należy również zauważyć, że wyjątki w Pythonie, choć szybkie w porównaniu do niektórych języków, są nadal wolniejsze niż normalne struktury sterowania przepływem, takie jak while i for.

To może być dużo pracy (choć może nie więcej niż już masz), ale gdybyś mógł wygenerować bajt kodu Pythona raczej niż Źródło Pythona, nie miałbyś problemu z implementacją goto, ponieważ bajt kodu Pythona (jak większość języków maszynowych psuedo) ma doskonale złożony kod opcode JUMP_ABSOLUTE.

 8
Author: kindall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-05 16:37:10

Zaktualizowałem mój Python goto decorator dla Pythona 3. Możesz go dostać pod adresem https://github.com/cdjc/goto . użycie goto zamiast funkcji może sprawić, że maszyna stanowa będzie około 5 razy szybsza.

Wersja dla Pythona 2 jest nadal dostępna pod adresem http://code.activestate.com/recipes/576944-the-goto-decorator / ale ma wiele błędów, które zostały naprawione w wersji Pythona 3.

 5
Author: cdjc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-19 20:21:02

Pojawią się pewne typowe wzorce, których kodowanie za pomocą goto prawdopodobnie będzie naśladować.

W większości przypadków podejrzewam, że wszystkie polecenia goto będą przeskakiwać do lokalizacji, która jest zarówno późniejsza, jak i w bardziej zamykającym się bloku; jeśli ciało funkcji doskonale podąża za tym wzorcem, przekształć polecenia goto w wyjątki, z etykietami jako bloki Exception.

Inne przypadki przeskakiwania goto z jednego miejsca do drugiego w tym samym bloku, jakie byłyby używane w maszynie stanowej. To może prawdopodobnie zostaną przetłumaczone na pętlę dyspozytorską; Każdy region między etykietą a następnym staje się funkcją; Goto ' S są zastępowane przez next_state = 'labelname'; return

Ostatni przypadek, który nie jest żadnym z powyższych i prawdopodobnie nie jest trywialny, to gdy skok jest do ciała pętli. Nie mam jeszcze odpowiedzi na to pytanie.

 1
Author: SingleNegationElimination,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-05 16:29:26

To nie jest dokładnie to, czego szukasz, ale wysłuchaj mnie.

Wiele lat temu, mój syn i ja napisaliśmy" przygodową " grę w BASIC. Każde miejsce w grze podziemnej było numerem linii. Kiedy opuściłeś jedno miejsce przez tunel biegnący na północ, na przykład, dotarłeś do innego miejsca.

Kodowanie było czymś w rodzaju if response == 'N' GOTO 2400. Zawodnicy kończyli na tym, że używali GOTOs.

Zastanawiałem się, jak można to zrobić w Pythonie i wpadłem na to.

Może taką technikę można wykorzystać do innych zastosowań, w których potrzebne jest coś takiego jak GOTO. Jeśli podzielisz swój program na kawałki, które są funkcjami, następujące" trochę głupie " kodowanie zrobiłby sztuczkę.

""" Simple, short and unfinished 'Adventure' game to show how a program such
as this with 'locations' (where each location is handled by a function) can
simulate 'GOTO's through use of the eval() function. Each time the player
chooses an exit from his current location, where he goes to will depend on the
exit chosen and achieved using eval() on the last line.

This saves having to code a series of 'if's at each location which call the
correct function the player goes to. Also, because the code always comes back
to the eval line at the botton each time, one location's function doesn't call
the next location's function, with possible risk of stack overflow if the
program is radically extended.

The program uses randint() to determine if characters are there and what they
are doing. This is just a taster. Dramatic improvements could be made if the
player could collect and use artefacts found during his travels. For instance
if there was a key somewhere and it was collected, it could be used to unlock
the door, but the key can't be picked up unless the troll isn't there etc.
The program needs to be able to parse (understand) simple natural language
(English) commands such as 'take key' or 'unlock door' or 'give food to troll'
There will also need to be some global variables so each function can behave
and do stuff dependent on these variables.

The program needs to be able to respond to players' commands, such as after a
player has indicated which direction he wants to go, the program responds,
'You can't go that way. the Ork is blocking your path'. You get the picture.

The program also needs to be able to save variables in a dictionary (which is
then pickled into a file) so players can close the game and save it and pick up
where they left off next time.

People new to this sort of game should realise by the way, that just because
a tunnel (or other route) leaves one location northwards, that doesn't mean
that it arrives at the next location from the south. The tunnels twist and
turn all over the place."""


def l0():
    #print('L0')
    print("You're south of a forbidding-looking cave")
    go = input('n or q > ')
    if go == 'n': return('1')
    if go == 'q': return('q')
    else: return 'q'

def l1():
    #print('L1')
    print("You're in a large, dark cave. Bats are hanging from the ceiling.")
    print("Tunnels lead north, east and west. The entrance is south of you.")
    go = input('n s e w > ')
    if go == 'n': return('3') # Leaving L1 northwards takes you to L3
    if go == 's': return('0') # Leaving L1 southwards takes you to L0
    if go == 'e': return('3') # Leaving L1 eastwards also takes you to L3
    if go == 'w': return('2') # Leaving L1 westwards takes you to L2
    else: return 'q'

def l2():
    #print('L2')
    print("You've come to a bridge running east across a chasm")
    print("On the other side the only way to go is through a tunnel")
    print("This side of the chasm, a tunnel leads north and a path leads south")
    go = input('n e s > ')
    if go == 'n': return('1') # As per L! but applicable to L2 etc.
    if go == 'e': return('4')
    if go == 's': return('3')
    else: return 'q'

def l3():
    #print('L3')
    print("You've come to a hot and humid cavern")
    print("Tunnels run north, east and west. A path leads south.")
    print("There's a dragon here.")
    dstate = randint(1,5)
    if dstate == 1: print("The dragon seems to be asleep")
    if dstate == 2: print("The dragon looks sleepy")
    if dstate == 3: print("The dragon is awake")
    if dstate == 4: print("The dragon looks angry")
    if dstate == 5: print("The dragon is breathing fire and very angry!")
    go = input('n s e w > ')
    if go == 'n': return('1')
    if go == 's': return('2')
    if go == 'e': return('4')
    if go == 'w': return('1')
    else: return 'q'

def l4():
    #print('L4')
    print("You've arrived at a grotto. There are jewels here!")
    tstate = randint(1,4)
    if tstate > 1: print("There's a troll here wielding a cudgel")
    print("Tunnels lead east, west and south from here")
    go = input('s e w > ')
    if go == 's': return('5')
    if go == 'e': return('2')
    if go == 'w': return('3')
    else: return 'q'

def l5():
    #print('L5')
    print("The tunnel ends at a door leading to a small room")
    print("Through a grille in the door, you can see there is no way out")
    print("The only way is back, south along the tunnel")
    print("But there's gold in the room!")
    print("The door is locked.")
    go = input('s > ')
    if go == 's': return('4')
    else: return 'q'

### ********************* Main Program Start ***************************

import random
from random import randint

go = l0()   # That's call L zero (location zero), not ten!

while go != 'q':
    print()
    go = eval("l"+go+"()")  # Program always returns here to sort out where to
                            # go next. Player doesn't of course!
 1
Author: John of York,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 15:18:12

Powstała wersja robocza: http://entrian.com/goto/.

Uwaga: był oferowany jako prima aprilisowy żart. (działa chociaż)

# Example 1: Breaking out from a deeply nested loop:
from goto import goto, label

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
print "Finished\n"
Nie trzeba dodawać. Tak, to zabawne, ale nie używaj go.
 0
Author: harmv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-09 22:57:39