Рекурсивное понимание списка в Python? - PullRequest
34 голосов
/ 14 апреля 2010

Можно ли определить рекурсивное понимание списка в 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

Возможно ли что-нибудь подобное?

Ответы [ 7 ]

33 голосов
/ 14 апреля 2010

Нет, нет никакого (документированного, надежного, стабильного, ... ;-) способа сослаться на «текущее понимание». Вы можете просто использовать цикл:

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

конечно, это очень дорого (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)

это очень быстро для очень длинных списков (O (N) вместо N в квадрате).

Редактировать : в Python 2.5 или 2.6 vars()['_[1]'] может фактически работать в той роли, которую вы хотите для self (для не вложенного listcomp) ... вот почему я уточнил свое утверждение пояснив, что нет документированного, надежного, стабильного способа получить доступ к «составляемому списку» - этому своеобразному, недокументированному «имени» '_[1]' (сознательно выбранный, чтобы не быть действительным идентификатором ;-) вершина «артефактов реализации» и любой код, полагающийся на него, заслуживают избавления от страданий; -).

11 голосов
/ 11 августа 2012

На самом деле вы можете! Этот пример с объяснением, надеюсь, проиллюстрирует, как.

определить рекурсивный пример, чтобы получить число только в том случае, если оно равно 5 или более, а если это не так, увеличьте его и снова вызовите функцию check. Повторяйте этот процесс, пока он не достигнет 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]
>>> 

по сути, две анонимные функции взаимодействуют следующим образом:

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
             }

Вам нужно сделать это, потому что вы не можете получить доступ к самой анонимной функции после выполнения.

т.е. 1017 *

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

, поэтому в этом примере пусть 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]
>>> 
1 голос
/ 05 мая 2019

Начиная с 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]

Это:

  • Инициализирует список acc, который символизирует текущий список уже увиденных элементов
  • Для каждого элемента проверяется, является ли он частью списка acc; а если нет:
    • добавляет элемент к acc (acc := acc + [x]) с помощью выражения присваивания
    • и в то же время использует новое значение acc в качестве сопоставленного значения для этого элемента
1 голос
/ 15 апреля 2010

Сделайте это:

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 голос
/ 14 апреля 2010

номер

Но похоже, что вы пытаетесь составить список уникальных элементов в числах.

Вы можете использовать set:

unique_items = set(nums)

Обратите внимание, что элементы в числах должны быть хэшируемыми.

Вы также можете сделать следующее. Что близко, насколько я могу понять, к вашей первоначальной идее. Но это не так эффективно, как создание set.

unique_items = []
for i in nums:
    if i not in unique_items:
        unique_items.append(i)
1 голос
/ 14 апреля 2010

Не уверен, что это то, что вы хотите, но вы можете написать вложения для вложенного списка:

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 голос
/ 14 апреля 2010

нет. это не будет работать, нет ссылки на self во время выполнения понимания списка.

И главная причина, конечно, заключается в том, что списки пониманий там, где они не предназначены для этого использования.

...