Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive list comprehension in Python?

Is it possible to define a recursive list comprehension in Python?

Possibly a simplistic example, but something along the lines of:

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 

Is anything like this possible?

like image 770
Yuval Adam Avatar asked Apr 14 '10 15:04

Yuval Adam


People also ask

Are list comprehensions recursive?

no. it won't work, there is no self to refer to while list comprehension is being executed. And the main reason of course is that list comprehensions where not designed for this use.

How do you recursively a list in Python?

You start with a full list, and your base case is when the list is empty. Traverse the list by passing the list in as an argument, using x. pop() to simultaneously fetch and remove the first item in the list, evaluate the popped item, and then pass the list (now shorter) into the same function.

What is a list comprehension in Python?

A Python list comprehension consists of brackets containing the expression, which is executed for each element along with the for loop to iterate over each element in the Python list. Python List comprehension provides a much more short syntax for creating a new list based on the values of an existing list.

Is list comprehension is possible in Python?

Python supports the following 4 types of comprehensions: List Comprehensions.


Video Answer


2 Answers

No, there's no (documented, solid, stable, ...;-) way to refer to "the current comprehension". You could just use a loop:

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

of course this is very costly (O(N squared)), so you can optimize it with an auxiliary set (I'm assuming that keeping the order of items in res congruent to that of the items in nums, otherwise set(nums) would do you;-)...:

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

this is enormously faster for very long lists (O(N) instead of N squared).

Edit: in Python 2.5 or 2.6, vars()['_[1]'] might actually work in the role you want for self (for a non-nested listcomp)... which is why I qualified my statement by clarifying there's no documented, solid, stable way to access "the list being built up" -- that peculiar, undocumented "name" '_[1]' (deliberately chosen not to be a valid identifier;-) is the apex of "implementation artifacts" and any code relying on it deserves to be put out of its misery;-).

like image 50
Alex Martelli Avatar answered Oct 02 '22 11:10

Alex Martelli


Actually you can! This example with an explanation hopefully will illustrate how.

define recursive example to get a number only when it is 5 or more and if it isn't, increment it and call the 'check' function again. Repeat this process until it reaches 5 at which point return 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] ] 

result:

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

essentially the two anonymous functions interact in this way:

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              } 

make g,f the 'same' function except that in one or both add a clause where the parameter is modified so as to cause the terminal condition to be reached and then go f(g,x) in this way g becomes a copy of f making it like:

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

You need to do this because you can't access the the anonymous function itself upon being executed.

i.e

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

so in this example let A = the first function and B the second. We call A passing B as f and i as v. Now as B is essentially a copy of A and it's a parameter that has been passed you can now call B which is like calling A.

This generates the factorials in a list

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] >>>  
like image 22
Henry Hollingworth Avatar answered Oct 02 '22 12:10

Henry Hollingworth