Bir python lambda ifadesinde birden çok ifade olması mümkün müdür?


112

Aşağıdakileri başarmaya çalışan bir python acemisiyim:

Bir liste listem var:

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

Her bir alt listeden yalnızca ikinci en küçük numarayı içeren başka bir listeye haritayı istiyorum. Yani sonuç şöyle olmalıdır:

[345, 465, 333]

Örneğin, en küçük sayı ile ilgileniyor olsaydım, şunları yapabilirdim:

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

Keşke bunu yapabilseydim:

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

ama sıralama zincirlemez. (Yok döndürür)

bunun gibi bir şeye de izin verilmez:

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

Bunu python'da map ile ancak adlandırılmış bir işlev tanımlamadan yapmanın bir yolu var mı ? (örneğin Ruby'deki anonim bloklarla kolaydır)


elbette bu mümkün olmalı .. belki yeni bir sürümde ...
ZEE

1
İfadeleri yürütemezsiniz, ancak lambda işlevinde işlevleri çağırabilirsiniz, bu nedenle pirtonik olmayan kirli hack lambda x: sort(x) OR x[1]işe yarar : Burada OR, ilk argümanını (dönüş değeri Yok) bir bool (=> False) olarak değerlendirir ve bu durumda VEYA ikinci argümanını döndürür. Ancak cevapların dediği gibi, lambda'dan kaçınsanız iyi olur.
Max

Yanıtlar:


136

Sorunuzdan daha genel endişelere kadar burada verebileceğim birkaç farklı cevap var. Yani en özelden en geneline:

S. Bir lambda'ya birden çok ifade koyabilir misiniz?

A. Hayır. Ama aslında bir lambda kullanmanıza gerek yok. İfadeleri defbunun yerine a koyabilirsiniz . yani:

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

map(second_lowest, lst)

S. Listeyi sıralayarak bir lambdadan ikinci en düşük öğeyi alabilir misiniz?

A. Evet. As Alex'in cevabı işaret, sorted()oldukça yerinde sıralama yerine, yeni bir liste oluşturur ve zincirleme edilebilir türden bir versiyonudur. Muhtemelen kullanmanız gereken şeyin bu olduğunu unutmayın - haritanızın orijinal listede yan etkilerinin olması kötü bir uygulamadır.

S. Bir liste dizisindeki her listeden ikinci en düşük öğeyi nasıl almalıyım?

A. sorted(l)[1] aslında bunun için en iyi yol değil. O (N log (N)) karmaşıklığına sahipken, bir O (n) çözümü mevcuttur. Bu, heapq modülünde bulunabilir.

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

Bu yüzden şunu kullan:

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

Ayrıca, lambdayı tamamen ortadan kaldıran bir liste kavramasının kullanılması genellikle daha net kabul edilir:

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

3
Heapq modülünde bulunan O (n) çözümü konusunda haklı olduğunuzu sanmıyorum. Orada tek yaptığınız, O (n log n) olan listeyi yığın halinde sıralamak ve ardından en küçük öğeleri bulmaktır.
avpx

8
Belgeler ( docs.python.org/2/library/heapq.html#heapq.nsmallest ) gerçekten de heapq.nsmallest () kullanmanın yalnızca sıralanmış () kullanmaktan daha az verimli olabileceği konusunda uyarır, bu nedenle yalnızca ölçümler hangi çözümün senin durumunda en hızlı. heapq.nsmallest (), O (k * log (n) + n) karmaşıklığına sahip olduğunu düşünüyorum, listenin uzunluğu n ve çıkarmak istediğiniz en küçük öğe sayısı k. Listeyi yığınlamak için O (n) ve k öğeleri pop yapmak için k kez O (log (n)). Bu, özellikle küçük k için O (n * log (n)) 'den daha iyidir.
Vortexfive

2
@Vortexfive ve sabit için k(burada 'ikinci en düşük' ile olduğu gibi) O(k*log(n)+n)basitleştirirO(n)
Duncan

5
Teknik olarak tek gömlekler için de bir lambda'ya ihtiyacınız yok, ancak kesinlikle daha kullanışlıdır. Umarım daha iyi lambda desteği daha sonra eklenir.
James

1
@avpx: Bu bir yığın değil. Tüm listeyi yığmak ve bir yığın sırasının yapacağı gibi tüm öğeleri patlatmak yerine, yalnızca görülen en küçük 2 öğenin 2 öğeli bir yığınını tutuyoruz.
user2357112 Monica'yı

81

İfadeleri bir listeye koymak birden çok ifadeyi simüle edebilir:

Örneğin:

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

9
Sadece açıklığa kavuşturmak için, işlev çağrıları Python'da ifade olarak kabul edilmez, ifadelerdir. Dolayısıyla bu liste, tümü olabilecek ifadelerin bir listesidir None.
Yawar

Bunun işe yaradığını düşünüyorum çünkü lambda'ya aktarılan koleksiyonu dinamik olarak analiz ederken, yorumlayıcı çağrılabilir imzaları algılar ve çalıştırır ... ile test edebilirsiniz -> lambda x: [print (x), print (x + 1), print (x +2)]
ZEE

5
Daha da iyisi: lambda x: [f1 (x), f2 (x)] [- 1], muhtemelen beklendiği gibi son ifadeden hesaplama sonucunu döndürecektir.
Anton Ovsyannikov

22

Zaman yolcusu burada. Genelde bir lambda içinde birden çok ifadeye sahip olmak istiyorsanız, diğer lambdaları o lambda'ya argüman olarak iletebilirsiniz.

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

Aslında birden çok ifadeye sahip olamazsınız, ancak lambdaları lambdalara geçirerek bunu simüle edebilirsiniz.

Düzenleme: Zaman yolcusu geri dönüyor! Ayrıca mantıksal ifadelerin davranışını (kısa devre yapan kuralları ve doğruluğu göz önünde bulundurarak) zincirleme işlemlere karşı kötüye kullanabilirsiniz. Üçlü operatörü kullanmak size daha da fazla güç verir. Yine, birden fazla ifadeye sahip olamazsınız , ancak elbette birçok işlev çağrınız olabilir. Bu örnek, bir sürü veriyle rastgele önemsiz şeyler yapıyor, ancak bazı komik şeyler yapabileceğinizi gösteriyor. Print deyimleri, None( .sort()yöntem gibi) dönen işlevlerin örnekleridir, ancak aynı zamanda ne lambdayaptığını göstermeye de yardımcı olurlar .

>>> (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ıralı işlevi şu şekilde kullanın :

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

Ah.Teşekkürler. (Fonksiyon için ne kadar açıklayıcı bir isim!) Lambda kısmındaki çoklu ifadeyi hala merak ediyorum. Mümkün?
ottodidakt

1
Hayır, "lambda formlarıyla oluşturulan işlevler ifade içeremez". docs.python.org/reference/expressions.html#lambda
alex vasi

1
-1: liste anlamaları yerine lambda ve harita? Pek pitonik değil.
nikow

@nikow [x [1] sıralanmış (liste)] için x [1] - pitonik yol?
RaGa__M

@Explorer_N: liste sıralanmamalı, ancak listenin öğeleri (listeler olan) mevcut problemde sıralanmalıdır. Yani daha doğrusu: [sorted(x)[1] for x in list_of_lists].
Maksimum

6

Aslında python'daki bir lambda ifadesinde birden çok ifadeye sahip olabilirsiniz. Tamamen önemsiz değil ama sizin örneğinizde şu işe yarıyor:

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

Her bir ifadenin herhangi bir şey döndürmediğinden veya içeri sardığında (.. ve False) emin olmalısınız. Sonuç, son değerlendirmenin getirdiği sonuçtur.

Misal:

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


4

Python'da birden çok ifadeyi tek bir ifadede birleştirmenin bir Hacky yolu, "ve" anahtar kelimesini kısa devre operatörü olarak kullanmaktır. Daha sonra bu tek ifadeyi doğrudan lambda ifadesinin bir parçası olarak kullanabilirsiniz.

Bu, bash gibi kabuk dillerinde kısa devre operatörü olarak "&&" kullanmaya benzer.

Ayrıca şunu unutmayın: Bir işlev ifadesini, işlevi kaydırarak her zaman gerçek bir değer döndürecek şekilde düzeltebilirsiniz.

Misal:

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

İkinci olarak düşünüldüğünde, başarı durumunda birçok işlev "0" veya Hiçbiri döndürdüğü için "ve" yerine "veya" kullanmak daha iyidir. Daha sonra yukarıdaki örnekte yer alan sarmalayıcı işlevinden kurtulabilirsiniz:

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

junky("a", "b")

've', ilk sıfır dönüş değerine ulaşıncaya kadar ifadeleri değerlendirecektir. daha sonra kısa devre yapar. 1 ve 1 ve 0 ve 1 şunları değerlendirir: 1 ve 1 ve 0 ve 1'i düşürür

'veya' operation, sıfır olmayan ilk dönüş değerine ulaşıncaya kadar ifadeleri değerlendirir. daha sonra kısa devre yapar.

0 veya 0 veya 1 veya 0, 0 veya 0 veya 1'i değerlendirir ve 0'ı düşürür


3

Veya lambda'dan kaçınmak ve liste yerine bir jeneratör kullanmak istiyorsanız:

(sıralama (sütun) [1] lst sütun için)


2

Size şanlı ama dehşet verici bir hack sunayım:

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)

Artık bu LETformu şu şekilde kullanabilirsiniz :

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

hangi verir: [345, 465, 333]



1

Bunu sort veya heapq kullanmak yerine min ve indeks kullanarak O (n) zamanında yapabilirsiniz.

İlk olarak, orijinal listenin minimum değeri dışındaki her şeyin yeni bir listesini oluşturun:

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

Ardından yeni listenin minimum değerini alın:

second_smallest = min(new_list)

Şimdi hep birlikte tek bir lambda:

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

Evet, gerçekten çirkin ama algoritmik olarak ucuz olmalı. Ayrıca bu konudaki bazı kişiler liste anlayışlarını görmek istediğinden:

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

1

Bu, bindbir Monad'daki işlevin tam olarak kullanıldığı şeydir .

bindİşlev ile birden fazla lambda'yı tek bir lambda'da birleştirebilirsiniz, her lambda bir ifadeyi temsil eder.


1

Aslında lambda'da birden çok ifadeyi kullanmanın bir yolu var. İşte benim çözümüm:

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

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

map(x, lst)

içindeki kod execakıllı bir IDE tarafından iç
gözlemlenemez

0

Evet. Bunu bu şekilde tanımlayabilir ve ardından birden çok ifadenizi aşağıdakilerle sarabilirsiniz:

Şema başlıyor:

başlangıç ​​= lambda * x: x [-1]

Ortak Lisp progn:

progn = lambda * x: x [-1]


0

Lambda işlevini kullanmadan daha iyi çözümler var. Ama gerçekten lambda işlevini kullanmak istiyorsak, burada birden çok ifadeyle başa çıkmak için genel bir çözüm var: map (lambda x: x [1] if (x.sort ()) else x [1], lst)

İfadenin ne getireceği umrunda değil.


0

Evet mümkün. Aşağıdaki kod parçacığını deneyin.

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

Size başka bir çözüm vereceğim, Lambda'nızın bir işlevi çağırmasını sağlayın.

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
Daha da iyisi: junky = multiple_statements.
Solomon Ucko
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.