2009-05-14 3 views
67

Я питон новичок пытается добиться следующего:Возможно ли иметь несколько операторов в выражении лямбда python?

У меня есть список списков:

lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]] 

Я хочу карту LST в другой список, содержащий только второй наименьшее число из каждой подсписка. Таким образом, результат должен быть:

[345, 465, 333] 

Например, если бы я был просто интересно наименьшим числом, я мог бы сделать:

map(lambda x: min(x),lst) 

Я хотел бы сделать это:

map(lambda x: sort(x)[1],lst) 

но сортировка не цепочка. (Возвращает None)

ни что-то вроде это позволило:

map(lambda x: sort(x); x[1],lst) #hence the multiple statement question 

Есть ли способ сделать это с картой в питоне, но без определения имени функции? (Легко с анонимными блоками в рубин, например)

+0

наверняка это должно быть возможно .. возможно в новой версии ... – ZEE

ответ

93

Есть несколько разных ответов, которые я могу здесь дать, от вашего конкретного вопроса до более общих проблем. поэтому от наиболее конкретных до наиболее общих:

Q. Можете ли вы добавить несколько высказываний в лямбда?

A. Нет. Но вам фактически не нужно использовать лямбда. Вы можете поместить эти утверждения в def. то есть:

def second_lowest(l): 
    l.sort() 
    return l[1] 

map(second_lowest, lst) 

Q. Вы можете получить второй самый низкий элемент из лямбда путем сортировки списка?

A. Да. Поскольку alex's answer poinst out, sorted() - это версия рода, которая создает новый список, а не сортировку на месте и может быть скована. Обратите внимание, что это, вероятно, то, что вы должны использовать. Плохая практика для вашей карты иметь побочные эффекты в исходном списке.

Q. Как получить второй самый нижний элемент из каждого списка в последовательности списков.

A.sorted(l)[1] на самом деле не лучший способ для этого. Он имеет сложность O (N log (N)), тогда как существует решение O (n). Это можно найти в модуле heapq.

>>> import heapq 
>>> l = [5,2,6,8,3,5] 
>>> heapq.nsmallest(l, 2) 
[2, 3] 

Так просто использовать:

map(lambda x: heapq.nsmallest(x,2)[1], list_of_lists) 

Это также обычно считается более ясным, чтобы использовать список понимание, что позволяет избежать лямбда вообще:

[heapq.nsmallest(x,2)[1] for x in list_of_lists] 
+12

+1: Вам не нужна лямбда. –

+3

Я не думаю, что вы правы в решении O (n), которое находится в модуле heapq. Все, что вы делаете, это куча, сортирующая список, то есть O (n log n), а затем поиск наименьших элементов. – avpx

+5

Документация (http://docs.python.org/2/library/heapq.html#heapq.nsmallest) действительно предупреждает, что использование heapq.nsmallest() может быть менее эффективным, чем просто использование sorted(), поэтому только измерения может сказать, какое решение является самым быстрым в вашем случае. Однако heapq.nsmallest() имеет сложность O (k * log (n) + n) Я думаю, с n длиной списка и k - количеством наименьших элементов, которые вы хотите извлечь. O (n), чтобы перечислить список и k раз O (log (n)) в поп-элементы. Это лучше, чем O (n * log (n)), особенно при малых k. – Vortexfive

7

Использование sorted function, как это:

map(lambda x: sorted(x)[1],lst) 
+0

ah.Thanks (что описательное имя для функции!) Тем не менее любопытно многократного заявление внутри. лямбда-часть. Возможное? – ottodidakt

+1

Нет, «функции, созданные с помощью лямбда-форм, не могут содержать операторов». http://docs.python.org/reference/expressions.html#lambda –

+1

-1: лямбда и карта вместо списков? Не очень pythonic. – nikow

3

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

(отсортировано (col) [1] для col in lst)

15

Путешественник времени здесь. Если вы вообще хотите иметь несколько операторов в лямбда, вы можете передать другие лямбды в качестве аргументов этой лямбда.

(lambda x, f: list((y[1] for y in f(x))))(lst, lambda x: (sorted(y) for y in x)) 

Вы не можете на самом деле есть несколько заявлений, но вы можете имитировать, что при прохождении лямбды до лямбды.

Редактировать: Путешественник во времени возвращается! Вы также можете злоупотреблять поведением булевых выражений (имея в виду правила короткого замыкания и правдивость) для операций цепочки. Использование тернарного оператора дает вам еще большую мощность. Опять же, вы не можете иметь несколько заявлений, но вы можете, конечно, иметь много вызовов функций. В этом примере делается какой-то произвольный хлам с кучей данных, но он показывает, что вы можете делать какие-то забавные вещи. Операторы печати являются примерами функций, которые возвращают None (как и метод .sort()), но они также помогают показать, что делает lambda.

>>> (lambda x: print(x) or x+1)(10) 
10 
11 
>>> f = (lambda x: x[::2] if print(x) or x.sort() else print(enumerate(x[::-1]) if print(x) else filter(lambda (i, y): print((i, y)) or (i % 3 and y % 2), enumerate(x[::-1])))) 
>>> from random import shuffle 
>>> l = list(range(100)) 
>>> shuffle(l) 
>>> f(l) 
[84, 58, 7, 99, 17, 14, 60, 35, 12, 56, 26, 48, 55, 40, 28, 52, 31, 39, 43, 96, 64, 63, 54, 37, 79, 25, 46, 72, 10, 59, 24, 68, 23, 13, 34, 41, 94, 29, 62, 2, 50, 32, 11, 97, 98, 3, 70, 93, 1, 36, 87, 47, 20, 73, 45, 0, 65, 57, 6, 76, 16, 85, 95, 61, 4, 77, 21, 81, 82, 30, 53, 51, 42, 67, 74, 8, 15, 83, 5, 9, 78, 66, 44, 27, 19, 91, 90, 18, 49, 86, 22, 75, 71, 88, 92, 33, 89, 69, 80, 38] 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] 
(0, 99) 
(1, 98) 
(2, 97) 
(3, 96) 
(4, 95) 
(5, 94) 
(6, 93) 
(7, 92) 
(8, 91) 
(9, 90) 
(10, 89) 
(11, 88) 
(12, 87) 
(13, 86) 
(14, 85) 
(15, 84) 
(16, 83) 
(17, 82) 
(18, 81) 
(19, 80) 
(20, 79) 
(21, 78) 
(22, 77) 
(23, 76) 
(24, 75) 
(25, 74) 
(26, 73) 
(27, 72) 
(28, 71) 
(29, 70) 
(30, 69) 
(31, 68) 
(32, 67) 
(33, 66) 
(34, 65) 
(35, 64) 
(36, 63) 
(37, 62) 
(38, 61) 
(39, 60) 
(40, 59) 
(41, 58) 
(42, 57) 
(43, 56) 
(44, 55) 
(45, 54) 
(46, 53) 
(47, 52) 
(48, 51) 
(49, 50) 
(50, 49) 
(51, 48) 
(52, 47) 
(53, 46) 
(54, 45) 
(55, 44) 
(56, 43) 
(57, 42) 
(58, 41) 
(59, 40) 
(60, 39) 
(61, 38) 
(62, 37) 
(63, 36) 
(64, 35) 
(65, 34) 
(66, 33) 
(67, 32) 
(68, 31) 
(69, 30) 
(70, 29) 
(71, 28) 
(72, 27) 
(73, 26) 
(74, 25) 
(75, 24) 
(76, 23) 
(77, 22) 
(78, 21) 
(79, 20) 
(80, 19) 
(81, 18) 
(82, 17) 
(83, 16) 
(84, 15) 
(85, 14) 
(86, 13) 
(87, 12) 
(88, 11) 
(89, 10) 
(90, 9) 
(91, 8) 
(92, 7) 
(93, 6) 
(94, 5) 
(95, 4) 
(96, 3) 
(97, 2) 
(98, 1) 
(99, 0) 
[(2, 97), (4, 95), (8, 91), (10, 89), (14, 85), (16, 83), (20, 79), (22, 77), (26, 73), (28, 71), (32, 67), (34, 65), (38, 61), (40, 59), (44, 55), (46, 53), (50, 49), (52, 47), (56, 43), (58, 41), (62, 37), (64, 35), (68, 31), (70, 29), (74, 25), (76, 23), (80, 19), (82, 17), (86, 13), (88, 11), (92, 7), (94, 5), (98, 1)] 
+2

От одного путешественника во времени к другому. Вы можете найти это интересным. Я использовал кучу трюков, в том числе ваш (в некотором смысле), для создания анонимных функций «многозадачность»: https://github.com/yawaramin/lambdak – Yawar

+2

Прохладный, но мой _eyes_. –

49

Выставление заявления в списке могут имитировать несколько операторов:

Например:

lambda x: [f1(x), f2(x), f3(x), x+1] 
+2

Спасибо! Это помогло мне. Возможно, для некоторых это полезно показать, как это работает: http://pastebin.com/JNquX1Kh –

+3

Чтобы уточнить, вызовы функций не считаются операторами в Python, это выражения. Таким образом, этот список - это всего лишь список выражений, которые могут быть «Нет». – Yawar

+0

Я думаю, что это работает, потому что при динамическом анализе коллекции, переданной лямбда, интерпретатор обнаруживает __callable__ подписи и выполняет их ... вы можете протестировать с помощью -> lambda x: [print (x), print (x + 1), print (x + 2)] – ZEE

1

Вы можете сделать это в O (N) времени с использованием мин и индекс вместо того, чтобы использовать то или heapq.

Сначала создайте новый список всего, кроме минимального значения исходного списка:

new_list = lst[:lst.index(min(lst))] + lst[lst.index(min(lst))+1:] 

Затем возьмите мин значение нового списка:

second_smallest = min(new_list) 

Теперь все вместе в одном лямбда :

map(lambda x: min(x[:x.index(min(x))] + x[x.index(min(x))+1:]), lst) 

Да, это действительно уродливо, но это должно быть алгоритмически дешево. Кроме того, так как некоторые люди в этой теме хотят увидеть списковые:

[min(x[:x.index(min(x))] + x[x.index(min(x))+1:]) for x in lst] 
1

Это именно то, что bind функции в Monad используется.

С функцией bind вы можете объединить несколько лямбда в одну лямбду, каждая лямбда представляет собой инструкцию.

2

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

map(lambda x: x.sort() or x[1],lst) 

Вы должны убедиться, что каждый оператор не возвращает ничего, или если это не завернуть его в (.. и False). Результатом является то, что возвращается последней оценкой.

Пример:

>>> f = (lambda : (print(1) and False) or (print(2) and False) or (print(3) and False)) 
>>> f() 
1 
2 
3 
2

Hacky способ объединить несколько операторов в одной инструкции в Python является использование «и» ключевое слово в качестве оператора короткого замыкания. Затем вы можете использовать этот единственный оператор непосредственно как часть выражения лямбда.

Это похоже на использование «& &» в качестве оператора короткого замыкания в языках оболочки, таких как bash.

Также обратите внимание: вы всегда можете исправить оператор функции, чтобы вернуть истинное значение, обернув функцию.

Пример:

def p2(*args): 
    print(*args) 
    return 1 # a true value 

junky = lambda x, y: p2('hi') and p2('there') and p2(x) and p2(y) 

junky("a", "b") 

На второй мысли, его, вероятно, лучше использовать «или» вместо «и», так как многие функции возвращают не «0» или Нет на успех. Тогда вы можете избавиться от функции упаковщика в приведенном выше примере:

junky = lambda x, y: print('hi') or print('there') or print(x) or print(y) 

junky("a", "b") 

«и» работать будет оценивать выражения, пока не дойдет до первого нулевого значения возврата. после чего он замыкается. 1 и 1 и 0 и 1 оценивает: 1 и 1 и 0, а капли 1

Операция «или» будет оценивать выражения до тех пор, пока они не получат первое ненулевое возвращаемое значение. после чего он замыкается.

0 или 0 или 1 или 0 оценивает 0 или 0 или 1, и капли 0

1

Я дам вам другое решение, сделать ваш лямбда-вызова функции.

def multiple_statements(x, y): 
    print('hi') 
    print('there') 
    print(x) 
    print(y) 
    return 1 

junky = lambda x, y: multiple_statements(x, y) 

junky('a', 'b'); 
+0

для меня, это самый простой и чистый способ иметь несколько операторов в лямбда-функции. – Jodo

0

Позвольте мне представить вам славный, но ужасающая хак:

import types 

def _obj(): 
    return lambda: None 

def LET(bindings, body, env=None): 
    '''Introduce local bindings. 
    ex: LET(('a', 1, 
      'b', 2), 
      lambda o: [o.a, o.b]) 
    gives: [1, 2] 

    Bindings down the chain can depend on 
    the ones above them through a lambda. 
    ex: LET(('a', 1, 
      'b', lambda o: o.a + 1), 
      lambda o: o.b) 
    gives: 2 
    ''' 
    if len(bindings) == 0: 
    return body(env) 

    env = env or _obj() 
    k, v = bindings[:2] 
    if isinstance(v, types.FunctionType): 
    v = v(env) 

    setattr(env, k, v) 
    return LET(bindings[2:], body, env) 

Теперь вы можете использовать эту LET форму как таковую:

map(lambda x: LET(('_', x.sort()), 
        lambda _: x[1]), 
    lst) 

, который дает: [345, 465, 333]

+0

Первоначально опубликовано здесь: https://gist.github.com/divs1210/d218d4b747b08751b2a232260321cdeb – divs1210

0

Фактически вы можете использовать несколько операторов в лямбда. Вот мое решение:

lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]] 

x = lambda l: exec("l.sort(); return l[1]") 

map(x, lst) 
Смежные вопросы