ドキュメントがバグってると思ったが違った話

If you don’t supply a value for *r* the length of the iterable is used, meaning that all the elements are permuted.

うそだぁ。

1 >>> from itertools import combinations
2 >>> combinations([3, 4, 1])
3 Traceback (most recent call last):
4   File "<stdin>", line 1, in <module>
5 TypeError: Required argument 'r' (pos 2) not found
6 >>> list(combinations([3, 4, 1], 3))
7 [(3, 4, 1)]
8 >>> list(combinations([3, 4, 1], 2))
9 [(3, 4), (3, 1), (4, 1)]

と思ったら。

あ、 combinations でなくて permutations 限定の話か、これ。

組み合わせも順列も、それこそ高校時代に習ったままの記憶しかなく、たまたま仕事でも必要になったことがなかったので、たぶん高校生向けと思われるサイトをみてようやく思い出した、という次第である。

それで気付いたことなのだが、「学生向けにわかりやすい説明」と思われる、「組み合わせは並べない順列は並べる」という説明は、ビミョーだ、ってことだ。

確かに「m 個から n 個選ぶ組み合わせ」「m 個から n 個選んだ n 個の並べ方」という問題を基準に考えればこの説明は「わかりやすい」のだけれど、同じ「並べる/並べない」という言葉で「真逆の雰囲気の説明」が出来てしまうのよ。Python のクックブックがまさにそれ。

まず組み合わせ(combinations)の説明:

:func:`itertools.combinations(iterable, r) ` は、 *iterable* から *r*-tuple 選択する全ての組み合わせを提供するイテレータを返します ::

そして、組み合わせが「順序を保つ」ことを説明し、順列(permutationss)の説明が続く:

それぞれのタプル内では、要素は *iterable* がそれを返したのと同じ順序を保ちます。例えば上の例であれば、 1 はいつでも 2, 3, 4, 5 の前に来ます。似たような関数に :func:`itertools.permutations(iterable, r=None) ` があり、こちらはこの順序についての制約がなく、 *r* 個選択する全ての順列を返します。

少し混乱してこないか? どっちが並べて、どっちが並べてない? どっちも並べてるんじゃない? とか。

冷静に、じっくりゆっくり考えればもちろんこれ、何一つ難しいことはないのね。「(3, 4, 1) と (1, 3, 4) が同じ意味かそうでないか」だけの差なんだから。それをなまじ「並べる/並べない」という脳内イメージを固定してしまいがちな言葉で説明しようとすると、こういう妙な言葉のトラップに引っかかる。

さて。ではなんで combinations は引数 r の省略を許さないのか。これが一瞬わからなくて、わかったときには、あぁ、バカだ、ワタシは、と思った。そりゃそーでしょーよ。「10 個から 10 個選ぶ組み合わせ」なんて計算したって意味ねーじゃん。