Подтвердить что ты не робот

`goto` в Python

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

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


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

4b9b3361

Ответ 1

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

xkcd GOTO

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

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

Декодер goto (рецепт Python от Carl Cerecke)

Это рецепт для вас, если вы устали от медленной скорости существующий goto модуль http://entrian.com/goto/. goto в этом рецепт примерно в 60 раз быстрее, а также более чистый (злоупотребление sys.settraceкажется едва ли пифоническим). Поскольку это декоратор, он предупреждает который использует функции goto. Он не реализует приход, но нетрудно расширить его, чтобы сделать это (упражнение для читателя). Кроме того, вычисляемые 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:

Ответ 2

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

Самый простой способ эмулировать forward 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 s.

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

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

Ответ 3

Я обновил свой 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.

Ответ 4

Появятся некоторые распространенные шаблоны, которые, скорее всего, будут использовать код goto.

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

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

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

Ответ 5

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

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

Кодирование было чем-то вроде 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 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 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 function doesn't call
the next location function, with possible risk of Qaru 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 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 a troll here wielding a cudgel")
    print("Tunnels lead east, west and south from here")
    go = input( 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 gold in the room!")
    print("The door is locked.")
    go = input( > ')
    if go == 's': return('4')
    else: return 'q'

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

import random
from random import randint

go = l0()   # That 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!

Ответ 6

Была сделана рабочая версия: 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"

Нет нужды говорить. Да, это смешно, но DONT его использовать.