こんな書き方があったのか!

先日の、順列を再帰で書く、というエントリの続き
インスパイヤ先↓
2008-02-26 - rindai87の日記
id:morchin氏によるコメント(morchinさん、毎度コメントありがとうございます。)

> これをグローバル変数使わずに書けないものか思案中。
以下のようにローカル側に一階層シフトするのはどうでしょう?

def permutation(n, m = 0):
  def _permutation(n, m, numList):
    if n == m:
      print numList
    else:
      for x in range(1, n+1):
        if x in numList:
          continue
        numList.append(x)
        _permutation(n, m + 1, numList)
        numList.pop()
  return _permutation(n, m, [])

Python初心者には

def _permutation(n, m, numList):

という書き方に面食らってしまった。

手元の「みんなのPython」には

 Pythonでは、名前の先頭にアンダースコア(_)が一つついたアトリビュートやメソッドはクラスの内部だけで利用するためにある、というルールのようなものがります。

と、あるので恐らくは関数内部で関数を定義するために必要なのかな、と推測しつつ、id:morchi氏による関数を実装。

def permutation(n, m = 0):
    def permutation2(n, m, numList):
        if n == m:
            print numList
        else:
            for x in range(1, n+1):
                if x in numList:
                    continue
                numList.append(x)
                _permutation(n, m + 1, numList)
                numList.pop()
    return permutation2(n, m, [])


def main():
    print permutation(5)


if __name__ == "__main__":
    main()

そして実行

D:\workspace\Python\2-28>python test.py
Traceback (most recent call last):
  File "test.py", line 20, in ?
    main()
  File "test.py", line 16, in main
    print permutation(3)
  File "test.py", line 12, in permutation
    return permutation2(n, m, [])
  File "test.py", line 10, in permutation2
    _permutation(n, m + 1, numList)
NameError: global name '_permutation' is not defined

!!!!!!!!!!!!!!
怒られた!?????
何でだ?
id:morchi氏の書いてくれたコードは、僕が最初に書いたコードの作業用配列と関数permitation()の関係を、関数permitation()の中に関数_permitation()を定義することで実現していると思われる。
納得できるだけに、何で動かないのか不明。
「みんなのPython」の記述によると、クラスにおけるメソッドについてだから、僕が考えてることと挙動がちょっと違うのかな。


で、いろいろ考えた末、導き出した一つの答え。

def permutation(n, numList, m=0):
    if n == m: 
        print numList
        numList = []
    else:
        for x in range(1, n+1):
            if x in numList:
                continue
            numList.append(x)
            permutation(n, numList, m+1)
            numList.pop()


def main():
    permutation(3, [])


if __name__ == "__main__":
    main()

かなりid:morchi氏のアイデアをパクっている…
実行結果

D:\workspace\Python\2-26>python permutation.py
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

一応うまく動いている。
作業用配列を渡しているだけ。
あんまりかっこよくはないけど、目標であった「グローバル変数を使わない」は達成。