web-dev-qa-db-ja.com

Pythonでの再帰的なリスト内包?

Pythonで再帰的なリスト内包表記を定義することは可能ですか?

おそらく単純な例ですが、次のようなものがあります。

nums = [1, 1, 2, 2, 3, 3, 4, 4]
willThisWork = [x for x in nums if x not in self] # self being the current comprehension

このようなことは可能ですか?

35
Yuval Adam

いいえ、「現在の理解」を参照する方法はありません(文書化され、堅実で、安定しています... ;-)。あなたはただループを使うことができます:

_res = []
for x in nums:
  if x not in res:
    res.append(x)
_

もちろん、これは非常にコストがかかるため(O(Nの2乗))、補助的なsetを使用して最適化できます(res内のアイテムの順序をの順序と一致させることを前提としていますnumsの項目、そうでない場合はset(nums)で十分です;-)...:

_res = []
aux = set()
for x in nums:
  if x not in aux:
    res.append(x)
    aux.add(x)
_

これは、非常に長いリスト(Nの2乗ではなくO(N))の場合に非常に高速です。

編集:Python 2.5または2.6では、vars()['_[1]']は実際にはselfに必要な役割で機能する可能性があります( -nested listcomp)...これが、「構築中のリスト」にアクセスする方法がないことを明確にすることでステートメントを修飾した理由です文書化された、堅実な、安定したその独特の文書化されていない「名前」_'_[1]'_(有効な識別子ではないように意図的に選択された;-)は「実装アーティファクト」の頂点であり、それに依存するコードはその悲惨さから解放されるに値します;-)。

33
Alex Martelli

実際にできます!説明付きのこの例は、うまくいけばその方法を説明します。

再帰的な例を定義して、5以上の場合にのみ数値を取得し、そうでない場合はインクリメントして、「チェック」関数を再度呼び出します。 5に達するまでこのプロセスを繰り返し、5に達すると5を返します。

print [ (lambda f,v: v >= 5 and v or f(f,v+1))(lambda g,i: i >= 5 and i or g(g,i+1),i) for i in [1,2,3,4,5,6] ]

結果:

[5, 5, 5, 5, 5, 6]
>>> 

基本的に、2つの無名関数は次のように相互作用します。

let f(g,x) = {  
                 expression, terminal condition
                 g(g,x), non-terminal condition
             }

let g(f,x) = {  
                 expression, terminal condition
                 f(f,x), non-terminal condition
             }

g、fを「同じ」関数にします。ただし、一方または両方で、パラメータが変更されて終了条件に到達する句を追加し、このようにしてf(g、x)に移動します。gはのコピーになります。 f次のようにします:

f(g,x) = {  
                 expression, terminal condition
                 {
                    expression, terminal condition,
                    g(g,x), non-terminal codition
                 }, non-terminal condition
             }

実行時に無名関数自体にアクセスできないため、これを行う必要があります。

つまり、

(lambda f,v: somehow call the function again inside itself )(_,_)

したがって、この例では、A =最初の関数、Bを2番目の関数とします。 Bをfとして、iをvとして渡すAを呼び出します。Bは本質的にAのコピーであり、渡されたパラメーターであるため、Aを呼び出すのと同じようにBを呼び出すことができます。

これにより、リストに階乗が生成されます

print [ (lambda f,v: v == 0 and 1 or v*f(f,v-1))(lambda g,i: i == 0 and 1 or i*g(g,i-1),i) for i in [1,2,3,5,6,7] ]

[1, 2, 6, 120, 720, 5040]
>>> 
11

これを行う:

nums = [1, 1, 2, 2, 3, 3, 4, 4]
set_of_nums = set(nums)
unique_num_list = list(set_of_nums)

またはこれさえ:

unique_num_list = sorted(set_of_nums)
1
hughdbrown

開始Python 3.8、および 代入式(PEP 572):= operator)は、式の結果に名前を付ける可能性を提供します。リスト内包内の変数を更新することで、すでに表示されている項目を参照できます。

# items = [1, 1, 2, 2, 3, 3, 4, 4]
acc = []; [acc := acc + [x] for x in items if x not in acc]
# acc = [1, 2, 3, 4]

この:

  • すでに見た要素の実行リストを象徴するリストaccを初期化します
  • 各アイテムについて、これはそれがすでにaccリストの一部であるかどうかをチェックします。そうでない場合:
    • アイテムをaccacc := acc + [x]代入式を介して
    • 同時に、このアイテムのマップ値としてaccの新しい値を使用します
1
Xavier Guihot

番号。それは機能しません。リスト内包表記の実行中に参照するselfはありません。

そしてもちろん、主な理由は、この用途のために設計されていないリスト内包表記です。

1
SilentGhost

これが必要かどうかはわかりませんが、ネストされたリスト内包表記を記述できます。

xs = [[i for i in range(1,10) if i % j == 0] for j in range(2,5)]
assert xs == [[2, 4, 6, 8], [3, 6, 9], [4, 8]]

コード例から、セットで実行できる重複を単純に排除したいようです。

xs = sorted(set([1, 1, 2, 2, 3, 3, 4, 4]))
assert xs == [1, 2, 3, 4]
1
Eli Courtwright

番号。

しかし、numsで一意の要素のリストを作成しようとしているようです。

setを使用できます:

unique_items = set(nums)

Numsの項目はハッシュ可能である必要があることに注意してください。

次のこともできます。私があなたの最初の考えに達することができるので、これは近いです。ただし、これはsetを作成するほど効率的ではありません。

unique_items = []
for i in nums:
    if i not in unique_items:
        unique_items.append(i)
1
Gary Kerr