# Recursive list comprehension in Python?

## Question:

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?

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.

Not sure if this is what you want, but you can write nested list comprehensions:

``````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]]
``````

From your code example, you seem to want to simply eliminate duplicates, which you can do with sets:

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

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)
``````

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

Edit: in Python 2.5 or 2.6, `vars()['_']` 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” `'_'` (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;-).

No.

But it looks like you are trying to make a list of the unique elements in nums.

You could use a `set`:

``````unique_items = set(nums)
``````

Note that items in nums need to be hashable.

You can also do the following. Which is a close as I can get to your original idea. But this is not as efficient as creating a `set`.

``````unique_items = []
for i in nums:
if i not in unique_items:
unique_items.append(i)
``````

Do this:

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

or even this:

``````unique_num_list = sorted(set_of_nums)
``````

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]
>>>
``````

Starting `Python 3.8`, and the introduction of assignment expressions (PEP 572) (`:=` operator), which gives the possibility to name the result of an expression, we could reference items already seen by updating a variable within the list comprehension:

``````# 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]
``````

This:

• Initializes a list `acc` which symbolizes the running list of elements already seen
• For each item, this checks if it’s already part of the `acc` list; and if not:
• appends the item to `acc` (`acc := acc + [x]`) via an assignment expression
• and at the same time uses the new value of `acc` as the mapped value for this item
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.