Apakah mungkin memiliki beberapa pernyataan dalam ekspresi lambda python?

112

Saya seorang pemula python yang mencoba mencapai yang berikut:

Saya punya daftar daftar:

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

Saya ingin memetakan lst ke dalam daftar lain yang hanya berisi nomor terkecil kedua dari setiap sublist. Jadi hasilnya seharusnya:

[345, 465, 333]

Misalnya jika saya hanya tertarik pada angka terkecil, saya dapat melakukan:

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

Saya berharap saya bisa melakukan ini:

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

tapi semacam tidak berantai. (mengembalikan Tidak ada)

hal seperti ini tidak diperbolehkan:

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

Apakah ada cara untuk melakukan ini dengan map dengan python tetapi tanpa mendefinisikan fungsi bernama ? (mudah dengan blok anonim di ruby, misalnya)

ottodidakt
sumber
pasti ini harus mungkin .. mungkin dalam versi baru ...
ZEE
1
Anda tidak dapat mengeksekusi pernyataan tetapi Anda dapat memanggil fungsi dalam fungsi lambda, sehingga peretasan kotor unpythonic lambda x: sort(x) OR x[1]akan berfungsi: Di ​​sini OR mengevaluasi argumen pertamanya (nilai kembalian None) sebagai bool (=> False), dan dalam kasus itu ATAU mengembalikan argumen keduanya. Tetapi seperti yang dikatakan jawabannya, lebih baik hindari lambda.
Maksimum

Jawaban:

136

Ada beberapa jawaban berbeda yang dapat saya berikan di sini, dari pertanyaan spesifik Anda hingga masalah yang lebih umum. Jadi dari yang paling spesifik hingga yang paling umum:

T. Dapatkah Anda meletakkan banyak pernyataan dalam lambda?

J. Tidak. Tapi Anda sebenarnya tidak perlu menggunakan lambda. Anda dapat meletakkan pernyataan di file def. yaitu:

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

map(second_lowest, lst)

T. Bisakah Anda mendapatkan item terendah kedua dari lambda dengan mengurutkan daftar?

A. Ya. Seperti yang ditunjukkan oleh jawaban alex , sorted()adalah versi sortir yang membuat daftar baru, bukan mengurutkan di tempat, dan dapat dirantai. Perhatikan bahwa ini mungkin yang seharusnya Anda gunakan - praktik yang buruk jika peta Anda memiliki efek samping pada daftar aslinya.

T. Bagaimana cara mendapatkan item terendah kedua dari setiap daftar dalam urutan daftar?

A. sorted(l)[1] sebenarnya bukan cara terbaik untuk ini. Ini memiliki kompleksitas O (N log (N)), sementara solusi O (n) ada. Ini dapat ditemukan di modul heapq.

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

Jadi gunakan saja:

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

Ini juga biasanya dianggap lebih jelas untuk menggunakan pemahaman daftar, yang menghindari lambda sama sekali:

[heapq.nsmallest(x,2)[1] for x in list_of_lists]
Brian
sumber
3
Saya tidak berpikir Anda benar tentang solusi O (n) yang ditemukan di modul heapq. Semua yang Anda lakukan di sana adalah menyortir daftar, yaitu O (n log n) dan kemudian mencari elemen terkecil.
avpx
8
Dokumentasi ( docs.python.org/2/library/heapq.html#heapq.nsmallest ) memang memperingatkan bahwa menggunakan heapq.nsmallest () mungkin kurang efisien daripada hanya menggunakan sort (), jadi hanya pengukuran yang dapat mengetahui solusi mana yang tercepat dalam kasus Anda. heapq.nsmallest () memang memiliki kompleksitas O (k * log (n) + n) menurut saya, dengan n panjang daftar dan k jumlah item terkecil yang ingin Anda ekstrak. O (n) untuk menumpuk daftar dan k kali O (log (n)) untuk memunculkan k item. Ini lebih baik daripada O (n * log (n)), terutama untuk k kecil.
Vortexfive
2
@Vortexfive dan untuk konstanta k(seperti di sini dengan 'terendah kedua') O(k*log(n)+n)disederhanakan menjadiO(n)
Duncan
5
Anda secara teknis juga tidak membutuhkan lambda untuk satu baris, tetapi itu pasti lebih nyaman. Semoga dukungan lambda yang lebih baik ditambahkan nanti.
Yakobus
1
@avpx: Ini bukan heapsort. Kami hanya mempertahankan tumpukan 2 elemen dari 2 item terkecil yang terlihat, daripada menumpuk seluruh daftar dan memunculkan semua elemen seperti yang akan dilakukan oleh heapsort.
user2357112 mendukung Monica
81

Menempatkan pernyataan dalam daftar dapat mensimulasikan beberapa pernyataan:

Misalnya:

lambda x: [f1(x), f2(x), f3(x), x+1]
jmkg
sumber
9
Hanya untuk memperjelas, pemanggilan fungsi tidak dianggap sebagai pernyataan dalam Python, mereka adalah ekspresi. Jadi daftar ini hanyalah daftar ekspresi, yang mungkin semuanya None.
Yawar
Saya pikir ini berfungsi karena ketika secara dinamis menganalisis koleksi yang diteruskan ke lambda, penerjemah mendeteksi tanda tangan yang dapat dipanggil dan menjalankannya ... Anda dapat menguji dengan -> lambda x: [print (x), print (x + 1), print (x +2)]
ZEE
5
Bahkan lebih baik: lambda x: [f1 (x), f2 (x)] [- 1], itu akan mengembalikan hasil perhitungan dari ekspresi terakhir, seperti yang mungkin diharapkan.
Anton Ovsyannikov
22

Penjelajah waktu di sini. Jika Anda secara umum ingin memiliki beberapa pernyataan dalam lambda, Anda bisa meneruskan lambda lain sebagai argumen ke lambda itu.

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

Anda sebenarnya tidak dapat memiliki beberapa pernyataan, tetapi Anda dapat mensimulasikannya dengan meneruskan lambda ke lambda.

Sunting: Penjelajah waktu kembali! Anda juga dapat menyalahgunakan perilaku ekspresi boolean (dengan mengingat aturan dan kejujuran arus pendek) ke operasi rantai. Menggunakan operator terner memberi Anda lebih banyak daya. Sekali lagi, Anda tidak dapat memiliki banyak pernyataan , tetapi tentu saja Anda dapat memiliki banyak pemanggilan fungsi. Contoh ini melakukan beberapa sampah sembarangan dengan sekumpulan data, tetapi, ini menunjukkan bahwa Anda dapat melakukan beberapa hal lucu. Pernyataan cetak adalah contoh fungsi yang kembali None(seperti halnya .sort()metode) tetapi juga membantu menunjukkan apa yang lambdadilakukannya.

>>> (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)]
2rs2ts
sumber
7

Gunakan fungsi yang diurutkan , seperti ini:

map(lambda x: sorted(x)[1],lst)
alex vasi
sumber
ah. Terima kasih. (nama yang deskriptif untuk fungsi tersebut!) Masih penasaran tentang pernyataan berganda dalam bagian lambda. Bisa jadi?
ottodidakt
1
Tidak, "fungsi yang dibuat dengan formulir lambda tidak dapat berisi pernyataan". docs.python.org/reference/expressions.html#lambda
alex vasi
1
-1: lambda dan map alih-alih pemahaman daftar? Tidak terlalu pythonic.
nikow
@nikow [x [1] untuk x diurutkan (daftar)] - cara pythonic?
RaGa__M
@ Explorer_N: bukan daftar yang harus diurutkan, tetapi elemen dari daftar (yang merupakan daftar) harus diurutkan dalam masalah saat ini. Jadi agak: [sorted(x)[1] for x in list_of_lists].
Maksimum
6

Anda sebenarnya dapat memiliki beberapa pernyataan dalam ekspresi lambda dengan python. Ini tidak sepenuhnya sepele tetapi dalam contoh Anda, karya-karya berikut ini:

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

Anda harus memastikan bahwa setiap pernyataan tidak mengembalikan apa pun atau jika pernyataan itu membungkusnya dengan (.. dan False). Hasilnya adalah apa yang dikembalikan oleh evaluasi terakhir.

Contoh:

>>> f = (lambda : (print(1) and False) or (print(2) and False) or (print(3) and False))
>>> f()
1
2
3
Loreno Heer
sumber
5

Menggunakan begin () dari sini: 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]
Janus Troelsen
sumber
4

Cara Hacky untuk menggabungkan beberapa pernyataan menjadi satu pernyataan di python adalah dengan menggunakan kata kunci "dan" sebagai operator sirkuit pendek. Kemudian Anda dapat menggunakan pernyataan tunggal ini secara langsung sebagai bagian dari ekspresi lambda.

Ini mirip dengan menggunakan "&&" sebagai operator sirkuit pendek dalam bahasa shell seperti bash.

Perhatikan juga: Anda selalu dapat memperbaiki pernyataan fungsi untuk mengembalikan nilai sebenarnya dengan membungkus fungsi.

Contoh:

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

Setelah dipikir-pikir, mungkin lebih baik menggunakan 'atau' daripada 'dan' karena banyak fungsi mengembalikan '0' atau Tidak ada saat berhasil. Kemudian Anda dapat menghilangkan fungsi pembungkus pada contoh di atas:

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

junky("a", "b")

'dan' operasikan akan mengevaluasi ekspresi hingga mencapai nilai pengembalian nol pertama. setelah itu korsleting. 1 dan 1 dan 0 dan 1 mengevaluasi: 1 dan 1 dan 0, dan menjatuhkan 1

'atau' operasikan akan mengevaluasi ekspresi hingga mencapai nilai pengembalian bukan nol pertama. setelah itu korsleting.

0 atau 0 atau 1 atau 0 mengevaluasi 0 atau 0 atau 1, dan menjatuhkan 0

Bill Moore
sumber
3

Atau jika Anda ingin menghindari lambda dan memiliki generator alih-alih daftar:

(diurutkan (col) [1] untuk kolom pertama)

odwl
sumber
2

Izinkan saya mempersembahkan kepada Anda peretasan yang mulia namun menakutkan:

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)

Anda sekarang dapat menggunakan LETformulir ini seperti:

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

pemberian yang mana: [345, 465, 333]

divs1210
sumber
Awalnya diposting di sini: gist.github.com/divs1210/d218d4b747b08751b2a232260321cdeb
divs1210
1

Anda dapat melakukannya dalam waktu O (n) menggunakan min dan indeks daripada menggunakan sort atau heapq.

Pertama buat daftar baru semuanya kecuali nilai min dari daftar asli:

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

Kemudian ambil nilai min dari daftar baru:

second_smallest = min(new_list)

Sekarang semuanya menjadi satu lambda:

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

Ya, ini benar-benar jelek, tetapi harus murah secara algoritme. Juga karena beberapa orang di utas ini ingin melihat pemahaman daftar:

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

Persis seperti inilah bindfungsi dalam sebuah Monad digunakan.

Dengan bindfungsi tersebut Anda dapat menggabungkan beberapa lambda menjadi satu lambda, setiap lambda mewakili sebuah pernyataan.

jhegedus.dll
sumber
1

Sebenarnya ada cara untuk menggunakan banyak pernyataan di lambda. Inilah solusi saya:

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

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

map(x, lst)
EternalCrafter_606
sumber
kode di dalamnya exectidak dapat
diintrospeksi
0

Iya. Anda dapat mendefinisikannya dengan cara ini dan kemudian membungkus beberapa ekspresi Anda dengan yang berikut:

Skema dimulai:

mulai = lambda * x: x [-1]

Progn Lisp umum:

progn = lambda * x: x [-1]

Anastasios
sumber
0

Ada solusi yang lebih baik tanpa menggunakan fungsi lambda. Tetapi jika kita benar-benar ingin menggunakan fungsi lambda, berikut adalah solusi umum untuk menangani beberapa pernyataan: map (lambda x: x [1] if (x.sort ()) else x [1], lst)

Anda tidak terlalu peduli apa yang dikembalikan pernyataan itu.

pengguna11166890
sumber
0

Ya itu mungkin. Coba potongan kode di bawah ini.

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()
Ravi
sumber
-1

Saya akan memberikan solusi lain, Jadikan lambda Anda memanggil fungsi.

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');
Bill Moore
sumber
7
Bahkan lebih baik: junky = multiple_statements.
Solomon Ucko