Чи можливо мати кілька висловлювань у виразі лямбда python?


112

Я новачок-пітон, який намагається досягти наступного:

У мене є список списків:

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)

але сортування не ланцюжок. (не повертає жодної)

і щось подібне не дозволено:

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

Чи можна це зробити з картою в python, але не визначаючи названу функцію ? (легко, наприклад, з анонімними блоками в рубіні, наприклад)


напевно, це має бути можливим .. можливо, у новій версії ...
ZEE

1
Ви не можете виконувати оператори, але ви можете викликати функції в лямбда-функції, тому непітонічний брудний хак lambda x: sort(x) OR x[1]спрацює: Тут АБО оцінює свій перший аргумент (повернене значення None) як bool (=> False), і в цьому випадку АБО повертає свій другий аргумент. Але як говориться у відповідях, краще уникати лямбда.
Макс

Відповіді:


136

Я можу дати тут декілька різних відповідей, від вашого конкретного запитання до більш загальних проблем. Отже, від найбільш конкретного до найбільш загального:

З. Чи можете ви помістити декілька висловлювань в лямбда?

А. Ні. Але насправді вам не потрібно використовувати лямбда. Ви можете defзамість цього поставити заяви . тобто:

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

map(second_lowest, lst)

Q. Чи можете ви отримати другий найнижчий предмет у лямбда, відсортувавши список?

A. Так. Як вказує відповідь Алекса, sorted()це версія сортування, яка створює новий список, а не сортування на місці, і може бути ланцюжком. Зауважте, що це, мабуть, саме ви повинні використовувати - це погана практика, щоб ваша карта мала побічні ефекти у вихідному списку.

З. Як я можу отримати другий найнижчий елемент із кожного списку в послідовності списків?

А. 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]

3
Я не думаю, що ви праві, що рішення O (n) знайдено в модулі heapq. Все, що ви там робите, це купа сортування списку, який є O (n log n), а потім пошук найменших елементів.
avpx

8
Документація ( docs.python.org/2/library/heapq.html#heapq.nsmallest ) дійсно попереджає, що використання heapq.nsmallest () може бути менш ефективним, ніж просто використання сортованого (), тому лише вимірювання можуть визначити, яке рішення є найшвидший у вашому випадку. Але heapq.nsmallest () має складність O (k * log (n) + n) Я думаю, з n довжиною списку та k кількістю найменших елементів, які ви бажаєте витягти. O (n) для заповнення списку і k разів O (log (n)) для появи поп-елементів. Це краще, ніж O (n * log (n)), особливо для малих k.
Vortexfive

2
@Vortexfive і для постійних k(як тут із "другим найнижчим") O(k*log(n)+n)спрощує доO(n)
Duncan

5
Технічно вам не потрібна лямбда для однолінійних, але це, звичайно, зручніше. Сподіваємося, краще підтримка лямбда надається згодом.
Джеймс

1
@avpx: Це не велика кількість. Ми підтримуємо лише 2-елементну купу з двох найменших побачених елементів, а не нагромаджуючи весь список і спливаючи всі елементи, як це робиться.
user2357112 підтримує Моніку

81

Розміщення операторів у списку може імітувати декілька висловлювань:

Наприклад:

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

9
Для уточнення, виклики функцій не вважаються операторами в Python, вони є виразами. Тож цей список - це лише перелік виразів, яким може бути все None.
Явар

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

5
Ще краще: lambda x: [f1 (x), f2 (x)] [- 1], вона поверне результат обчислення останнього виразу, як, ймовірно, очікувалося.
Антон Овсянніков

22

Тут мандрівник в часі. Якщо ви хочете мати декілька висловлювань в межах лямбда, ви можете передати інші лямбда як аргументи до цієї лямбда.

(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)]

7

Використовуйте сортовану функцію , наприклад:

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

ah.Thanks. (яке описове ім'я для функції!) Досі цікаво щодо декількох висловлювань у лямбда-частині. Можливо?
ottodidakt

1
Ні, "функції, створені за допомогою лямбда-форм, не можуть містити заяви". docs.python.org/reference/expressions.html#lambda
alex

1
-1: лямбда і карта замість розуміння списку? Не дуже пітонічний.
nikow

@nikow [x [1] for x у сортованому (списку)] - пітонічному способі?
RaGa__M

@Explorer_N: список не повинен бути відсортований, але елементи списку (які є списками) повинні бути відсортовані у цій проблемі. Так , а: [sorted(x)[1] for x in list_of_lists].
Макс

6

Насправді ви можете мати декілька висловлювань у лямбдаському виразі в 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

5

Використовуючи функцію start () звідси: http://www.reddit.com/r/Python/comments/hms4z/ask_pyreddit_if_you_were_making_your_own/c1wycci

Python 3.2 (r32:88445, Mar 25 2011, 19:28:28) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]
>>> begin = lambda *args: args[-1]
>>> list(map(lambda x: begin(x.sort(), x[1]), lst))
[345, 465, 333]

4

Хекі-спосіб поєднання декількох висловлювань в одне твердження в 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


3

Або якщо ви хочете уникати лямбда і мати генератор замість списку:

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


2

Дозвольте представити вам славний, але жахливий хак:

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]


Спочатку розміщено тут: gist.github.com/divs1210/d218d4b747b08751b2a232260321cdeb
divs1210

1

Ви можете зробити це за O (n) час, використовуючи min та index замість сортування або 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функції ви можете об'єднати кілька лямбда в одну лямбда, кожна лямбда представляє твердження.


1

Насправді існує спосіб використання декількох висловлювань в лямбда. Ось моє рішення:

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

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

map(x, lst)

код всередині execне може бути представлений інтелектуальним IDE
javadba

0

Так. Ви можете визначити це таким чином, а потім обернути свої декілька виразів наступним чином:

Схема починається:

початок = лямбда * х: х [-1]

Поширений прогрес Lisp:

прогн = лямбда * х: х [-1]


0

Є кращі рішення без використання лямбда-функції. Але якщо ми дійсно хочемо використовувати функцію лямбда, тут є загальне рішення для вирішення кількох висловлювань: map (lambda x: x [1] if (x.sort ()) else x [1], lst)

Вас не цікавить, що повертається у виписці.


0

Так, це можливо. Спробуйте нижче фрагмент коду.

x = [('human', 1), ('i', 2), ('am', 1), ('.', 1), ('love', 1), ('python', 3), ('', 1),
  ('run', 1), ('is', 2), ('robust', 1), ('hello', 1), ('spark', 2), ('to', 1), ('analysis', 2), ('on', 1), ('big', 1), ('data', 1), ('with', 1), ('analysis', 1), ('great', 1)
]

rdd_filter = rdd1_word_cnt_sum.filter(lambda x: 'python' in x or 'human' in x or 'big' in x)
rdd_filter.collect()

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

7
Ще краще: junky = multiple_statements.
Соломон Учко
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.