Python эквивалент списка?
Какой лучший эквивалент Python для функции Common Lisp maplist
? Из документации по списку карт:
maplist похож на mapcar, за исключением того, что функция применяется к последовательному подсписок списков. функция сначала применялись к самим спискам, а затем в cdr каждого списка и затем в cdr cdr каждого список и т.д.
Пример (псевдокод, не проверен):
>>> def p(x): return x
>>> maplist(p, [1,2,3])
[[1, 2, 3], [2, 3], [3]]
Примечание: аргументы, переданные в p
в приведенном выше примере, будут списками [1, 2, 3]
, [2, 3]
, [3]
; т.е. p
не применяется к элементам этих списков. Например:.
>>> maplist(lambda l: list(reversed(l)), [1,2,3])
[[3, 2, 1], [3, 2], [3]]
Ответы
Ответ 1
Вы можете написать небольшую функцию для этого
def maplist(func, values):
return [map(func, values[i:]) for i in xrange(len(values))]
>>> maplist(lambda a: a* 2, [1,2,3])
[[2, 4, 6], [4, 6], [6]]
[изменить]
если вы хотите применить функцию в подсписках, вы можете изменить эту функцию следующим образом:
def maplist(func, values):
return [func(values[i:]) for i in xrange(len(values))]
>>> maplist(lambda l: list(reversed(l)), [1,2,3])
[[3, 2, 1], [3, 2], [3]]
Ответ 2
Как упоминалось в @Cybis и других, вы не можете сохранить сложность O (N) с помощью списков Python; вам нужно создать связанный список. Рискуя доказать 10-е правило Greenspun, вот такое решение:
class cons(tuple):
__slots__=()
def __new__(cls, car, cdr):
return tuple.__new__(cls, (car,cdr))
@classmethod
def from_seq(class_, l):
result = None
for el in reversed(l):
result = cons(el, result)
return result
@property
def car(self): return self._getitem(0)
@property
def cdr(self): return self._getitem(1)
def _getitem(self, i):
return tuple.__getitem__(self, i)
def __repr__(self):
return '(%s %r)' % (self.car, self.cdr)
def __iter__(self):
v = self
while v is not None:
yield v.car
v = v.cdr
def __len__(self):
return sum(1 for x in self)
def __getitem__(self, i):
v = self
while i > 0:
v = v.cdr
i -= 1
return v.car
def maplist(func, values):
result = [ ]
while values is not None:
result.append(func(values))
values = values.cdr
return result
Результаты тестирования:
>>> l = cons.from_seq([1,2,3,4])
>>> print l
(1 (2 (3 (4 None))))
>>> print list(l)
[1, 2, 3, 4]
>>> print maplistr(lambda l: list(reversed(l)), cons.from_seq([1,2,3]))
[[3, 2, 1], [3, 2], [3]]
EDIT: это решение на основе генератора, которое в основном решает одну и ту же проблему без использования связанных списков:
import itertools
def mapiter(func, iter_):
while True:
iter_, iter2 = itertools.tee(iter_)
iter_.next()
yield func(iter2)
Результаты тестирования:
>>> print list(mapiter(lambda l: list(reversed(list(l))), [1,2,3]))
[[3, 2, 1], [3, 2], [3]]
Ответ 3
Eewww... Нарезка списка - это операция с линейным временем. Все решения, опубликованные до сих пор, имеют временную сложность O (n ^ 2). Lisp maplist, я считаю, имеет O (n).
Проблема заключается в том, что тип списка Python не является связанным списком. Это динамически изменяемый размер массива (т.е. Как векторный тип "С++ STL" ).
Если для вас важна линейная временная сложность, невозможно создать функцию "списка карт" над списками Python. Было бы лучше изменить свой код для работы с индексами в списке или преобразовать список в фактический связанный список (все еще операция с линейным временем, но будет иметь много накладных расходов).
Ответ 4
это работает как ваш пример (я изменил код reyjavikvi)
def maplist(func, l):
result=[]
for i in range(len(l)):
result.append(func(l[i:]))
return result
Ответ 5
Вы можете использовать вложенные списки:
>>> def p(x): return x
>>> l = range(4)[1:]
>>> [p([i:]) for i in range(len(l))]
[[1, 2, 3], [2, 3], [3]]
Что вы можете использовать для определения самого списка:
>>> def maplist(p, l): return [p([i:]) for i in range(len(l))]
>>> maplist(p, l)
[[1, 2, 3], [2, 3], [3]]
Ответ 6
O (N) реализация maplist()
для связанных списков
maplist = lambda f, lst: cons(f(lst), maplist(f, cdr(lst))) if lst else lst
Смотрите вопрос Связанный список Python.
Ответ 7
У Python есть связанный тип списка, называемый deque
:
from collections import deque
def maplist(f, lst):
linked = deque(lst)
results = []
while linked:
results.append(f(linked))
linked.popleft() # assumes left is head, as in lisp
return results
In [134]: maplist(lambda l: list(reversed(l))*2, [1,2,3])
Out[134]: [[3, 2, 1, 3, 2, 1], [3, 2, 3, 2], [3, 3]]
Линейное время, делает то же самое, будет иметь те же характеристики производительности, что и операции над списками lisp (т.е. изменение внутренних элементов будет линейным по длине изменяемого списка) и той же семантикой (т.е. если f
изменяет список, он изменяет его для всех последующих исполнений f
внутри этого вызова maplist
, однако, поскольку это создает копию lst
, f
не может изменить lst
, в отличие от Common Lisp).
Ответ 8
Изменение ответа Аарона:
In [8]: def maplist(p, l): return [p([x for x in l[i:]]) for i in range(len(l))]
...:
In [9]: maplist(lambda x: x + x, [1,2,3])
Out[9]: [[1, 2, 3, 1, 2, 3], [2, 3, 2, 3], [3, 3]]
Ответ 9
Я думаю, что нет, но может работать следующая функция:
def maplist(func, l):
for i in range(len(l)):
func(l[i:])