web-dev-qa-db-ja.com

Pythonの `goto`

Pythonではgotoを使用する必要があります。私は entrians goto を見つけましたが、私のPython実装(Mac上のCPython 2.7.1))にはこのモジュールがないため、移植可能です。少なくとも、すべてのPython CPythonバイトコードをサポートする実装で機能するはずです(特に、CPythonとPyPyに関心があります)。次に この関連する質問 、そして cdjc's goto 。そして、以下の回答で与えられるもの。

手動でバイトコードを作成することができます(つまり、独自のPythonコンパイラ)を記述します)。そのような命令(JUMP_ABSOLUTE と友達)。しかし、もっと簡単な方法はあるのでしょうか。 inspect経由で単一のバイトコード命令を呼び出すことは可能ですか? Pythonを介してコンパイルし、生成されたPythonバイトコードに自動的にパッチを適用することも検討しました。


もちろん、なぜ本当に必要なのかを説明しないと、人々は理由を尋ね、私に役立つ答えをくれません。つまり、私のユースケースでは、C AST to Python ASTに翻訳してこれをコンパイルしています。 Pythonコード。gotoを除くすべて。関連プロジェクト: PyCParser (を参照) interpreter.py )、 PyCPythonPyLua

29
Albert

私は誰もが何を考えているか知っています:

xkcd GOTO

ただし、実際にgotoが必要な教訓的なケースがあるかもしれません。

このpythonレシピは、関数デコレータとしてgotoコマンドを提供します。

gotoデコレータPythonレシピby Carl Cerecke

既存のgotoモジュール http://entrian.com/goto/ の遅い速度にうんざりしている場合、これはあなたのためのレシピです。このレシピのgotoは約60倍高速であり、クリーンでもあります(_sys.settrace_の乱用はほとんどPythonicではないようです)。これはデコレータであるため、gotoを使用する関数をリーダーに警告します。これを拡張することは難しくありませんが、comefromコマンドは実装していません(読者のための演習)。また、計算されたgotoはサポートされていません。彼らはPythonicではありません。

  • 関数のバイトコード逆アセンブリを表示するには、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つの追加の実装があります:

44
Paolo Moretti

あなたがPythonでgotoを必要とするためにこれまでに見た唯一の有効なユースケースを持っているかもしれません。 :-)

Pythonでフォワードgotoをエミュレートする最も簡単な方法は、ネストされた制御構造のどの深さからでもジャンプできるため、例外を使用することです。

class Goto(Exception):
    pass

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

複数の宛先をサポートする必要がある場合、これは厄介ですが、ネストされたtry/except構造と複数の例外クラスを使用して、宛先ごとに1つずつ行うことができると思います。 Cはgotoを単一の関数のスコープに制限しているので、少なくともこれを機能させる方法について心配する必要はありませんacross関数。 :-)もちろん、リバースgotosでは機能しません。

Pythonの例外は、一部の言語に比べると高速ですが、whileforなどの通常のフロー制御構造よりも低速です。

これは多くの作業になる可能性がありますが(おそらく、あなたが既に取り組んでいる以上のものではありません)、Pythonソースではなく、Pythonバイトコードを生成できる場合Pythonバイトコード(ほとんどの疑似マシン言語と同様))には完全にJUMP_ABSOLUTEオペコードがあるため、gotoの実装に問題はありません。

8
kindall

python goto decorator for Python 3.を更新しました。これは https://github.com/cdjc/gotoで入手できます。 。関数の代わりにgotoを使用すると、ステートマシンを約5倍高速化できます。

python 2のバージョンは http://code.activestate.com/recipes/576944-the-goto-decorator/ で引き続き利用できますが、番号がありますpython 3バージョンで修正されたバグの数。

5
cdjc

gotoを使用するコードが従う可能性が高い一般的なパターンがいくつかあります。

ほとんどの場合、私はすべてのgotoステートメントが後で、より囲みのあるブロックの両方の場所にジャンプするのではないかと思います。関数本体がこのパターンに完全に従う場合は、gotoを例外に変換し、ラベルをexceptブロックにします。

ステートマシンで使用される、同じブロック内のある場所から別の場所へジャンプする他のケース。これはおそらくディスパッチループに変換できます。ラベルと次の間の各領域が関数になります。 gotoはnext_state = 'labelname'; returnに置き換えられました

上記のいずれでもない、おそらく自明ではない最後のケースは、ジャンプがintoループ本体である場合です。まだ答えはありません。

これはまさにあなたが探しているものではありませんが、私に聞いてください。

何年も前、息子と私はBASICで「アドベンチャー」ゲームを書きました。アンダーグラウンドゲームの各場所は行番号でした。たとえば、北に向かうトンネルを通って1つの場所を離れると、別の場所に到着しました。

コーディングは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
John of York

動作するバージョンが作成されました: 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"

言うまでもなく。はい、面白いですが、それを使用しないでください。

0
harmv