`goto` в Python - PullRequest
       59

`goto` в Python

28 голосов
/ 05 августа 2011

Я должен использовать goto в Python.Я нашел entrians goto, но моя реализация Python (CPython 2.7.1 на Mac) не имеет этого модуля, поэтому он не выглядит переносимым.По крайней мере, он должен работать во всех реализациях Python, которые поддерживают байт-код CPython (особенно я забочусь о CPython и PyPy).Тогда есть этот связанный вопрос , и cdjc's goto.И те, что приведены в ответах ниже.

Я мог бы пойти и создать байт-код вручную (т.е. написать свой собственный компилятор Python), потому что есть такая инструкция (JUMP_ABSOLUTE и друзья).Но мне интересно, есть ли более простой способ.Можно ли через inspect или около того вызывать одну инструкцию байт-кода?Я также думал о компиляции через Python, а затем автоматически исправлял сгенерированный байт-код Python.


Конечно, люди спросят, почему, и не дадут мне никакого полезного ответа, если я не объясню, почему я действительнонужно это.Короче говоря, мой вариант использования: я перевожу C AST в Python AST и компилирую это.Я могу каким-то образом сопоставить каждый логический поток (все циклы и прочее) с эквивалентным кодом Python.Все, кроме goto.Связанные проекты: PyCParser (см. interpreter.py), PyCPython , PyLua .

Ответы [ 6 ]

44 голосов
/ 05 августа 2011

Я знаю, что все думают:

xkcd GOTO

Однако могут быть некоторые дидактические случаи, когда вам действительно нужно goto.

Этот рецепт Python предоставляет команду goto в качестве декоратора функции.

Goto Decorator ( Рецепт Python от Carl Cerecke )

Это рецепт для вас, если вы устали от медленной скорости существующий goto модуль http://entrian.com/goto/. goto в этом рецепт примерно в 60 раз быстрее, а также чище (злоупотребление sys.settrace кажется вряд ли питонным). Поскольку это декоратор, он предупреждает читатель который использует функции goto. Это не реализует Comefrom команда, хотя это не трудно продлить это сделать (упражнение для читателя). Кроме того, вычисленные gotos не поддерживаются; они не вещий.

  • Используйте dis.dis(fn), чтобы показать дизассемблирование функции с помощью байт-кода.
  • Байт-коды функции доступны по fn.func_code.co_code. Это только для чтения так:
  • Декорированная функция создается точно так же, как и старая, но с обновленным байт-кодом, чтобы подчиняться командам goto.
  • Это только 2.x; новый модуль отсутствует в Python 3.x (другой упражнение для читателя!)

Использование

@goto
def test1(n):
    s = 0

    label .myLoop

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

    goto .myLoop

>>> test1(10)
55

Обновление

Вот две дополнительные реализации, совместимые с Python 3:

8 голосов
/ 05 августа 2011

У вас может быть единственный действительный вариант использования, который я когда-либо видел, для необходимости goto в Python. :-)

Самый простой способ эмуляции вперед goto в Python - это использование исключений, поскольку они могут выпрыгнуть из любой глубины вложенных структур управления.

class Goto(Exception):
    pass

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

Это становится проблематично, если вам нужно поддерживать более одного пункта назначения, но я думаю, что это можно сделать, используя вложенные структуры try/except и несколько классов исключений, по одному для каждого пункта назначения. Поскольку C ограничивает goto областью действия одной функции, по крайней мере вам не придется беспокоиться о том, как заставить эту работу работать в функциях. :-) Конечно, это не работает для обратного goto с.

Следует также отметить, что исключения в Python, хотя и бывают быстрыми по сравнению с некоторыми языками, все же медленнее, чем обычные структуры управления потоком, такие как while и for.

Это может быть много работы (хотя, возможно, не больше, чем вы уже), но если бы вы могли генерировать байт-код Python, а не исходный код Python, у вас не возникло бы проблем с реализацией goto, потому что байт-код Python ( как и большинство псевдо-машинных языков), имеет совершенно кодовый JUMP_ABSOLUTE код операции.

5 голосов
/ 20 августа 2014

Я обновил свой python goto decorator для Python 3. Вы можете получить его по адресу https://github.com/cdjc/goto. Использование goto вместо функций может сделать конечный автомат примерно в 5 раз быстрее.

Версия для Python 2 по-прежнему доступна на http://code.activestate.com/recipes/576944-the-goto-decorator/, но в ней есть ряд ошибок, исправленных в версии Python 3.

1 голос
/ 25 января 2013

Это не совсем то, что вы ищете, но выслушайте меня.

Много лет назад мы с сыном написали игру "Приключение" на бейсике. Каждая локация в подпольной игре представляла собой номер строки. Например, когда вы покинули одно место через туннель, идущий на север, вы попали в другое место.

Кодирование было что-то вроде if response == 'N' GOTO 2400. Таким образом, игроки в конечном итоге ходили повсюду, используя GOTO.

Я задавался вопросом, как это можно сделать в Python, и придумал это.

Возможно, такую ​​технику можно использовать для других приложений, где требуется что-то вроде GOTO. Если вы разделите вашу программу на части, которые являются функциями, то следующее «немного глупое» кодирование поможет вам.

""" 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 голос
/ 05 августа 2011

Будут некоторые общие шаблоны, которым код, использующий goto, скорее всего будет следовать.

В большинстве случаев я подозреваю, что все операторы goto перейдут в местоположение, которое будет как позже, так и в более закрывающий блок;Если тело функции точно следует этому шаблону, преобразуйте goto в исключения, с метками в качестве исключающих блоков.

Другие случаи перехода goto из одного места в другое в том же блоке, как это будет использовано вконечный автомат.Это, вероятно, может быть переведено в цикл диспетчеризации;каждая область между меткой и следующей становится функцией;goto заменяются на next_state = 'labelname'; return

Последний случай, который не является ни одним из вышеперечисленных и, возможно, нетривиален, это когда прыжок в тело цикла.У меня пока нет ответа на этот вопрос.

0 голосов
/ 10 февраля 2017

Создана рабочая версия: http://entrian.com/goto/.

Примечание: оно было предложено как первоапрельская шутка. (хотя работает)

# 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"

Излишне говорить. Да, это смешно, но НЕ используйте его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...