views:

278

answers:

5

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?

A: 

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.

SilentGhost
A: 

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]
Eli Courtwright
+11  A: 

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;-).

Alex Martelli
The set operations make it O(n log(n)), I'm pretty sure.
dash-tom-bang
@dash-tom-bang sets in Python aren't implemented as red-black trees (like in STL), but use hashes instead as far as I know so it will be O(N).
Justin Peel
@Justin is correct -- python sets and dicts are well-optimized hashes, with O(1) amortized cost for adding items and O(1) cost for lookups.
Alex Martelli
+1  A: 

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)
PreludeAndFugue
+1  A: 

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)
hughdbrown
The list comprehensions are unnecessary. `unique_num_list = list(set_of_nums)`. `sorted(set_of_nums)` returns a list.
PreludeAndFugue
@PreludeAndFugue: good points. I'll change the code.
hughdbrown