Python中的递归列表理解?

Yuv*_*dam 34 python list-comprehension

是否有可能在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
Run Code Online (Sandbox Code Playgroud)

有可能这样吗?

Ale*_*lli 33

不,没有(记录的,稳固的,稳定的,...... ;-)方式来引用"当前的理解".你可以使用一个循环:

res = []
for x in nums:
  if x not in res:
    res.append(x)
Run Code Online (Sandbox Code Playgroud)

当然这是非常昂贵的(O(N平方)),所以你可以用辅助来优化它set(我假设保持物品的顺序与物品的顺序res一致nums,否则set(nums)就会你;-). ..:

res = []
aux = set()
for x in nums:
  if x not in aux:
    res.append(x)
    aux.add(x)
Run Code Online (Sandbox Code Playgroud)

对于非常长的列表(O(N)而不是N平方),这非常快.

编辑:在Python 2.5或2.6中,vars()['_[1]']可能实际上可以使用您想要的角色self(对于非嵌套的listcomp)...这就是为什么我通过澄清没有记录,可靠,稳定的方式访问"列表"来限定我的语句的原因正在建立" - 那个奇特的,无证件的"名称" '_[1]'(故意选择不是有效的标识符;-)是"实施工件"的顶点,任何依赖它的代码都应该摆脱它的痛苦;-) .

  • 集合操作使它成为 O(n log(n)),我很确定。 (2认同)
  • Python中的@ dash-tom-bang集并没有实现为红黑树(如STL中所示),但据我所知使用哈希值,因此它将是O(N). (2认同)

Xav*_*hot 13

开始Python 3.8,并引入赋值表达式(PEP 572):=运算符),它提供了命名表达式结果的可能性,我们可以通过更新列表理解中的变量来引用已经看到的项目:

# 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]
Run Code Online (Sandbox Code Playgroud)

这:

  • 初始化一个列表acc,表示已见过的元素的运行列表
  • 对于每个项目,这会检查它是否已经是列表的一部分acc;如果不:
    • 通过赋值表达式将项目附加到acc( acc := acc + [x])
    • 同时使用 的新值acc作为此项的映射值


Hen*_*rth 11

其实你可以!这个带有解释的例子有望说明如何.

定义递归示例以仅在数字为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] ]
Run Code Online (Sandbox Code Playgroud)

结果:

[5, 5, 5, 5, 5, 6]
>>> 
Run Code Online (Sandbox Code Playgroud)

基本上这两个匿名函数以这种方式交互:

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
             }
Run Code Online (Sandbox Code Playgroud)

使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
             }
Run Code Online (Sandbox Code Playgroud)

您需要执行此操作,因为您在执行时无法访问匿名函数.

(lambda f,v: somehow call the function again inside itself )(_,_)
Run Code Online (Sandbox Code Playgroud)

所以在这个例子中,让A =第一个函数,B代表第二个函数.我们称A传递B为f而i为v.现在因为B本质上是A的副本而且它是一个已经传递的参数,你现在可以调用B,就像调用A.

这会在列表中生成阶乘

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]
>>> 
Run Code Online (Sandbox Code Playgroud)

  • 克隆lambda是不必要的; 您可以使用通用代理作为第一个lambda来允许任何类型的第二个lambda调用自身.`(lambda f,arg:f(f,arg))(lambda self,v:....,firstvalue)` (2认同)