続続☆重複順列

重複順列の話 その3


前回イテレータクラスを見てて、なんかまた別のジェネレータに出来そうだと思った。
それで出来たのがこれ。

#[NonRecurseGenPerm]
def repeated_permutation_n(dimension, n):
    buf = [0] * dimension
    max_val = n - 1
    max_dim = dimension - 1
    range_list = range(dimension)
    while True:
        yield buf
        for i in range_list:
            if buf[i] < max_val:
                buf[i] += 1
                break
            if i < max_dim:
                buf[i] = 0
                continue
            return

def repeated_permutation_a(dimension, iterable):
    values = _get_seq(iterable)    # _get_seq は前回参照
    for L in repeated_permutation_n(dimension, len(values)):
        yield map(lambda i:values[i], L)

def repeated_permutation_b(dimension, iterable):
    values = _get_seq(iterable)
    value_buf = [values[0] for i in xrange(dimension)]
    buf = [0] * dimension
    max_val = len(values) - 1
    max_dim = dimension - 1
    range_list = range(dimension)
    i = 0
    while True:
        for j in range_list[:i+1]:
            value_buf[j] = values[buf[i]]
        yield value_buf
        for i in range_list:
            if buf[i] < max_val:
                buf[i] += 1
                break
            if i < max_dim:
                buf[i] = 0
                continue
            return


それぞれ、前回のイテレータクラス RepeatedPermutationN,A,B をジェネレータにしたもの。
そういえば、ジェネレータ構文ってイテレータを簡単に作れるようにするって目的を持っていたっけ。
それにしても固定長バッファをごにょごにょしていると「あー俺って手続き脳だなぁ」と思い知らされる。
関数名がかぶるのでモジュールを分けることにしました。一行目がモジュール名。後でまとめてパフォーマンステストする予定。

本命!! 再帰 + yield でジェネレータ

前回保留にした、再帰とyieldを使ったジェネレータ構文ですが、結局 web で見つけてしまいました。

M.Hiroi's さん家の「お気楽 Python プログラミング入門」:●ジェネレータ関数の再帰定義より転載。

def gen_perm(nums, n = 0):
    if n == len(nums):
        yield []
    else:
        for x in gen_perm(nums, n + 1):
            for y in nums:
                if y not in x:
                    yield x + [y]  #(1)


そうそう、まさにやりたかったこと!
再帰関数がサブシーケンスを返して、それに1要素追加して上へ返す」ってのは分かてたんですけど、
#(1) のところが思い付けなかったみたいです。
このサイト勉強になるサイトで、昔 xyzzy lisp 覚えるのにお世話になりました。
やっぱり、lisp脳はエレガントだな〜。


で、上は重複の無い順列で、次元数(リストの長さ)が指定できないので、これまでのプログラムに合わせて改造させていただきました。

#[RecurseGenPerm]
def repeated_permutation(dimension, iterable, n=0):
    if n == dimension:
        yield []
    else:
        for subseq in repeated_permutation(dimension, iterable, n + 1):
            for item in iterable:
                yield subseq + [item]

ふう、これでもう思い残すことは無い。


といつつその4へ続く。