Рекурсивное понимание списка в Python?

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

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


person Yuval Adam    schedule 14.04.2010    source источник
comment
Если порядок не проблема, list(set(num)). В противном случае отметьте unique_everseen в docs.python.org/library/itertools.html.   -  person kennytm    schedule 14.04.2010
comment
оповещение о дырявой абстракции. По моему мнению, понимания не следует рассматривать как циклы, даже если они могут быть реализованы как циклы в cpython.   -  person wim    schedule 16.04.2013


Ответы (7)


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

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]' (преднамеренно выбранное, чтобы не быть действительным идентификатором;-) является вершиной "артефактов реализации" и любой код, полагающийся на него, заслуживает избавления от страданий ;-).

person Alex Martelli    schedule 14.04.2010
comment
Операции над множествами делают это O (n log (n)), я почти уверен. - person dash-tom-bang; 14.04.2010
comment
Наборы @dash-tom-bang в Python не реализованы как красно-черные деревья (как в STL), а, насколько я знаю, вместо этого используют хэши, поэтому это будет O (N). - person Justin Peel; 14.04.2010
comment
@Justin прав - наборы и словари python представляют собой хорошо оптимизированные хэши с амортизированной стоимостью O (1) для добавления элементов и стоимостью O (1) для поиска. - person Alex Martelli; 15.04.2010

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

определите рекурсивный пример, чтобы получить число, только если оно равно 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] ]

результат:

[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
             }

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

i.e

(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]
>>> 
person Henry Hollingworth    schedule 11.08.2012
comment
Клонирование лямбды не требуется; вы можете использовать общий прокси-сервер в качестве первой лямбды, чтобы позволить любой второй лямбде вызывать себя. (lambda f,arg: f(f,arg))(lambda self,v: .... , firstvalue) - person Sean Gugler; 03.11.2014

Начало 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, который символизирует текущий список уже просмотренных элементов.
  • 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
    • и в то же время использует новое значение acc в качестве сопоставленного значения для этого элемента
person Xavier Guihot    schedule 05.05.2019
comment
Действительно аккуратно, обязательно воспользуюсь в будущем - person Sebastian Serrano; 16.11.2019

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

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]
person Eli Courtwright    schedule 14.04.2010

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

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

person SilentGhost    schedule 14.04.2010

No.

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

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

unique_items = set(nums)

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

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

unique_items = []
for i in nums:
    if i not in unique_items:
        unique_items.append(i)
person Gary Kerr    schedule 14.04.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)
person hughdbrown    schedule 14.04.2010
comment
Понимание списка не нужно. unique_num_list = list(set_of_nums). sorted(set_of_nums) возвращает список. - person Gary Kerr; 15.04.2010