Có thể có nhiều câu lệnh trong một biểu thức lambda python không?


112

Tôi là một người mới chơi trăn đang cố gắng đạt được những điều sau:

Tôi có một danh sách các danh sách:

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

Tôi muốn bản đồ lst vào một danh sách khác chỉ chứa số nhỏ thứ hai từ mỗi danh sách con. Vì vậy, kết quả sẽ là:

[345, 465, 333]

Ví dụ: nếu tôi chỉ quan tâm đến số nhỏ nhất, tôi có thể làm:

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

Tôi ước tôi có thể làm điều này:

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

nhưng sắp xếp không chuỗi. (trả về Không có)

những thứ như thế này không được phép:

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

Có cách nào để làm điều này với bản đồ trong python nhưng không xác định một hàm được đặt tên không? (ví dụ: thật dễ dàng với các khối ẩn danh trong ruby)


chắc chắn điều này sẽ khả thi .. có thể trong một phiên bản mới ...
ZEE

1
Bạn không thể thực hiện các câu lệnh nhưng bạn có thể gọi các hàm trong hàm lambda, do đó, cách hack bẩn thỉu không phức tạp lambda x: sort(x) OR x[1]sẽ hoạt động: Ở đây OR đánh giá đối số đầu tiên của nó (giá trị trả về None) dưới dạng bool (=> False) và trong trường hợp đó OR trả về đối số thứ hai của nó. Nhưng như câu trả lời đã nói, tốt hơn nên tránh lambda.
Tối đa

Câu trả lời:


136

Tôi có thể đưa ra một số câu trả lời khác nhau ở đây, từ câu hỏi cụ thể của bạn đến những mối quan tâm chung chung hơn. Vì vậy, từ cụ thể nhất đến chung chung nhất:

Q. Bạn có thể đặt nhiều câu lệnh trong lambda không?

A. Không. Nhưng bạn không thực sự cần sử dụng lambda. Bạn có thể đặt các câu lệnh trong một defthay thế. I E:

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

map(second_lowest, lst)

Q. Bạn có thể nhận được mặt hàng thấp thứ hai từ lambda bằng cách sắp xếp danh sách không?

A. Có. Như câu trả lời của alex đã chỉ ra, sorted()là một phiên bản sắp xếp tạo ra một danh sách mới, thay vì sắp xếp tại chỗ và có thể được xâu chuỗi. Lưu ý rằng đây có thể là thứ bạn nên sử dụng - việc bản đồ của bạn có các tác dụng phụ trong danh sách ban đầu là một việc làm không tốt.

Q. Làm cách nào để lấy mục thấp thứ hai từ mỗi danh sách trong một chuỗi danh sách?

A. sorted(l)[1] không thực sự là cách tốt nhất cho việc này. Nó có độ phức tạp O (N log (N)), trong khi tồn tại một nghiệm O (n). Điều này có thể được tìm thấy trong mô-đun heapq.

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

Vì vậy, chỉ cần sử dụng:

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

Nó cũng thường được coi là rõ ràng hơn khi sử dụng khả năng hiểu danh sách, điều này tránh hoàn toàn lambda:

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

3
Tôi không nghĩ bạn nói đúng về giải pháp O (n) được tìm thấy trong mô-đun heapq. Tất cả những gì bạn đang làm ở đó là sắp xếp đống danh sách, là O (n log n) và sau đó tìm các phần tử nhỏ nhất.
avpx 3/12/12

8
Tài liệu ( docs.python.org/2/library/heapq.html#heapq.nsmallest ) thực sự cảnh báo rằng việc sử dụng heapq.nsmallest () có thể kém hiệu quả hơn so với việc chỉ sử dụng sorted (), vì vậy chỉ các phép đo mới có thể cho biết giải pháp nào là nhanh nhất trong trường hợp của bạn. Tuy nhiên, heapq.nsmallest () có độ phức tạp là O (k * log (n) + n) Tôi nghĩ, với n là độ dài của danh sách và k là số mục nhỏ nhất bạn muốn trích xuất. O (n) để xếp chồng danh sách và k lần O (log (n)) để bật k mục. Điều này tốt hơn O (n * log (n)), đặc biệt là đối với k nhỏ.
Vortexfive

2
@Vortexfive và cho hằng số k(như ở đây với 'mức thấp thứ hai') O(k*log(n)+n)đơn giản hóa thànhO(n)
Duncan

5
Về mặt kỹ thuật, bạn cũng không cần lambda cho một lớp lót, nhưng nó chắc chắn tiện lợi hơn. Hy vọng rằng hỗ trợ lambda tốt hơn sẽ được thêm vào sau này.
James

1
@avpx: Nó không phải là một đống. Chúng tôi chỉ duy trì một đống 2 phần tử trong số 2 mục nhỏ nhất được nhìn thấy, thay vì xếp đống toàn bộ danh sách và bật tất cả các phần tử như một heapsort sẽ làm.
user2357112 hỗ trợ Monica

80

Đặt các câu lệnh trong một danh sách có thể mô phỏng nhiều câu lệnh:

Ví dụ:

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

9
Chỉ cần làm rõ, các cuộc gọi hàm không được coi là các câu lệnh trong Python, chúng là các biểu thức. Vì vậy, danh sách này chỉ là danh sách các biểu thức, có thể là tất cả None.
Yawar

Tôi nghĩ điều này hoạt động vì khi phân tích động bộ sưu tập được chuyển đến lambda, trình thông dịch sẽ phát hiện các chữ ký có thể gọi và thực thi chúng ... bạn có thể kiểm tra với -> lambda x: [print (x), print (x + 1), print (x +2)]
ZEE

5
Thậm chí tốt hơn: lambda x: [f1 (x), f2 (x)] [- 1], nó sẽ trả về biểu thức kết quả tính toán cuối cùng, như mong đợi.
Anton Ovsyannikov

22

Du hành thời gian ở đây. Nếu bạn thường muốn có nhiều câu lệnh trong một lambda, bạn có thể chuyển các lambda khác làm đối số cho lambda đó.

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

Bạn thực sự không thể có nhiều câu lệnh, nhưng bạn có thể mô phỏng điều đó bằng cách chuyển lambdas cho lambdas.

Chỉnh sửa: Người du hành thời gian trở lại! Bạn cũng có thể lạm dụng hành vi của biểu thức boolean (ghi nhớ các quy tắc ngắn mạch và tính trung thực) cho các hoạt động chuỗi. Sử dụng toán tử bậc ba cung cấp cho bạn nhiều sức mạnh hơn. Một lần nữa, bạn không thể có nhiều câu lệnh , nhưng tất nhiên bạn có thể có nhiều lệnh gọi hàm. Ví dụ này thực hiện một số rác tùy ý với một loạt dữ liệu, nhưng, nó cho thấy rằng bạn có thể làm một số điều thú vị. Các câu lệnh in là ví dụ về các hàm trả về None(cũng như .sort()phương thức) nhưng chúng cũng giúp hiển thị những gì lambdađang làm.

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

Sử dụng chức năng đã sắp xếp , như thế này:

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

ah. Cảm ơn. (tên mô tả cho hàm!) Vẫn tò mò về câu lệnh bội trong phần lambda. Khả thi?
ottodidakt

1
Không, "các hàm được tạo bằng biểu mẫu lambda không được chứa câu lệnh". docs.python.org/reference/expressions.html#lambda
alex vasi

1
-1: lambda và bản đồ thay vì hiểu danh sách? Không phải là rất đáng sợ.
nikow

@nikow [x [1] for x in sorted (list)] - pythonic way?
RaGa__M

@Explorer_N: không phải danh sách phải được sắp xếp, nhưng các phần tử của danh sách (là danh sách) phải được sắp xếp trong bài toán hiện tại. Vì vậy, thay vì: [sorted(x)[1] for x in list_of_lists].
Tối đa

6

Trên thực tế, bạn có thể có nhiều câu lệnh trong một biểu thức lambda trong python. Nó không hoàn toàn tầm thường nhưng trong ví dụ của bạn, những điều sau đây hoạt động:

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

Bạn phải đảm bảo rằng mỗi câu lệnh không trả về bất cứ điều gì hoặc nếu nó có bao hàm (.. và False). Kết quả là những gì được trả về bởi lần đánh giá cuối cùng.

Thí dụ:

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


4

Một cách Hacky để kết hợp nhiều câu lệnh thành một câu lệnh duy nhất trong python là sử dụng từ khóa "và" làm toán tử ngắn mạch. Sau đó, bạn có thể sử dụng câu lệnh đơn này trực tiếp như một phần của biểu thức lambda.

Điều này tương tự như việc sử dụng "&&" làm toán tử ngắn mạch trong các ngôn ngữ shell như bash.

Cũng lưu ý: Bạn luôn có thể sửa một câu lệnh hàm để trả về giá trị true bằng cách gói hàm.

Thí dụ:

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")

Theo suy nghĩ thứ hai, có lẽ tốt hơn nên sử dụng 'hoặc' thay vì 'và' vì nhiều hàm trả về '0' hoặc Không có khi thành công. Sau đó, bạn có thể thoát khỏi hàm wrapper trong ví dụ trên:

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

junky("a", "b")

'và' hoạt động sẽ đánh giá các biểu thức cho đến khi nó đạt đến giá trị trả về 0 đầu tiên. sau đó nó ngắn mạch. 1 và 1 và 0 và 1 đánh giá: 1 và 1 và 0, và giảm 1

Thao tác 'hoặc' sẽ đánh giá các biểu thức cho đến khi nó đạt giá trị trả về khác 0 đầu tiên. sau đó nó ngắn mạch.

0 hoặc 0 hoặc 1 hoặc 0 đánh giá 0 hoặc 0 hoặc 1 và giảm xuống 0


3

Hoặc nếu bạn muốn tránh lambda và có một trình tạo thay vì một danh sách:

(đã sắp xếp (col) [1] cho col trong lst)


2

Để tôi giới thiệu cho bạn một bản hack huy hoàng nhưng đáng sợ:

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)

Bây giờ bạn có thể sử dụng LETbiểu mẫu này như sau:

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

mang lại: [345, 465, 333]



1

Bạn có thể làm điều đó trong thời gian O (n) bằng cách sử dụng min và index thay vì dùng sort hoặc heapq.

Đầu tiên, hãy tạo danh sách mới của mọi thứ ngoại trừ giá trị tối thiểu của danh sách ban đầu:

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

Sau đó lấy giá trị tối thiểu của danh sách mới:

second_smallest = min(new_list)

Bây giờ tất cả cùng nhau trong một lambda duy nhất:

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

Vâng, nó thực sự xấu, nhưng nó phải rẻ về mặt thuật toán. Cũng vì một số người trong chủ đề này muốn xem toàn bộ danh sách:

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

1

Đây chính xác là những gì bindhàm trong Đơn nguyên được sử dụng.

Với bindhàm, bạn có thể kết hợp nhiều lambda thành một lambda, mỗi lambda đại diện cho một câu lệnh.


1

Thực ra có một cách bạn có thể sử dụng nhiều câu lệnh trong lambda. Đây là giải pháp của tôi:

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

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

map(x, lst)

các bên trong mã execkhông thể introspected bởi một IDE thông minh
javadba

0

Đúng. Bạn có thể xác định nó theo cách này và sau đó kết hợp nhiều biểu thức của bạn với những điều sau:

Chương trình bắt đầu:

begin = lambda * x: x [-1]

Progn Lisp chung:

progn = lambda * x: x [-1]


0

Có nhiều giải pháp tốt hơn mà không cần sử dụng hàm lambda. Nhưng nếu chúng ta thực sự muốn sử dụng hàm lambda, đây là một giải pháp chung để xử lý nhiều câu lệnh: map (lambda x: x [1] if (x.sort ()) else x [1], lst)

Bạn không thực sự quan tâm những gì câu lệnh trả về.


0

Có nó là có thể. Hãy thử đoạn mã dưới đây.

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

Tôi sẽ cung cấp cho bạn một giải pháp khác, Làm cho lambda của bạn gọi một hàm.

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
Thậm chí tốt hơn: junky = multiple_statements.
Solomon Ucko
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.