Pythonラムダ式に複数のステートメントを含めることは可能ですか?


112

私は次のことを達成しようとしているpython初心者です:

リストのリストがあります:

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

各サブリストから2番目に小さい番号のみを含む別のリストに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
ステートメントを実行することはできませんが、ラムダ関数で関数を呼び出すことができるので、非Pythonicのダーティーハックlambda x: sort(x) OR x[1]が機能します。2番目の引数を返します。しかし、答えが言うように、ラムダを避けた方がいいです。
最大

回答:


136

特定の質問からより一般的な懸念事項まで、私がここで提供できるいくつかの異なる回答があります。最も具体的なものから最も一般的なものまで:

Q.複数のステートメントをラムダに入れることはできますか?

A.いいえ。ただし、実際にはラムダを使用する必要はありません。def代わりにステートメントを置くことができます。つまり:

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

map(second_lowest, lst)

Q.リストを並べ替えることにより、ラムダから2番目に低いアイテムを取得できますか?

A.はい。アレックスの答えは指摘し、sorted()むしろその場でソートよりも、新しいリストを作成し、連鎖させることができます種類のバージョンがあります。これはおそらくあなたが使用すべきものであることに注意してください-マップが元のリストに副作用を及ぼすことは悪い習慣です。

Q.一連のリストの各リストから2番目に低い項目を取得するにはどうすればよいですか?

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]

3
heapqモジュールでO(n)ソリューションが見つかるのは、あなたが正しいとは思いません。そこで行うことは、リストを並べ替えるヒープ(O(n log n))を作成し、最小の要素を見つけることだけです。
avpx

8
ドキュメント(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))をk回実行します。これは、特に小さなkの場合、O(n * log(n))よりも優れています。
Vortexfive 2013

2
@Vortexfiveおよび定数k(ここでは「2番目に低い」とする)はO(k*log(n)+n)単純化しますO(n)
Duncan

5
技術的にはワンライナーにもラムダは必要ありませんが、確かにより便利です。うまくいけば、より良いラムダサポートが後で追加されるでしょう。
James

1
@avpx:ヒープソートではありません。リスト全体をヒープ化し、ヒープソートのようにすべての要素をポップするのではなく、表示される2つの最小アイテムの2要素のヒープのみを維持します。
user2357112は2017

81

ステートメントをリストに入れると、複数のステートメントをシミュレートできます。

例えば:

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

9
明確にするために、関数呼び出しはPythonのステートメントとは見なされず、式です。したがって、このリストは式のリストにすぎませんNone
Yawar

ラムダに渡されたコレクションを動的に分析すると、インタプリタが呼び出し可能な署名を検出して実行するため、これはうまくいくと思います...- >ラムダxでテストできます:[print(x)、print(x + 1)、print(x +2)]
ZEE、2016

5
さらに良い:ラムダx:[f1(x)、f2(x)] [-1]、おそらく予想通り、最後の式から計算結果を返します。
アントンOvsyannikov

22

ここに時間旅行者。通常、ラムダ内に複数のステートメントが必要な場合は、他のラムダを引数としてそのラムダに渡すことができます。

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

実際には複数のステートメントを持つことはできませんが、ラムダをラムダに渡すことでそれをシミュレートできます。

編集:タイムトラベラーが帰ってきた!また、ブール式の動作を悪用して(短絡ルールと真実性を考慮して)、操作を連鎖させることもできます。三項演算子を使用すると、さらに強力になります。繰り返しになりますが、複数のステートメントを持つことはできませんが、もちろん多くの関数呼び出しを持つことができます。この例では、大量のデータを使用して任意のジャンクを実行していますが、面白いことができることを示しています。printステートメントは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 2009年

1
いいえ、「ラムダフォームで作成された関数にはステートメントを含めることができません」。docs.python.org/reference/expressions.html#lambda
alex vasi

1
-1:リスト内包表記の代わりにラムダとマップ?それほどpythonicではありません。
nikow 2009年

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

@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


4

Pythonで複数のステートメントを1つのステートメントに結合するHackyな方法は、「and」キーワードを短絡演算子として使用することです。次に、この単一のステートメントをラムダ式の一部として直接使用できます。

これは、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」またはNoneを返すため、「and」ではなく「or」を使用する方がおそらく良いでしょう。その後、上記の例のラッパー関数を取り除くことができます。

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

junky("a", "b")

「and」operateは、最初のゼロの戻り値に到達するまで式を評価します。その後、短絡します。1と1と0と1は、1と1と0を評価し、1をドロップします。

「or」operateは、最初のゼロ以外の戻り値に到達するまで式を評価します。その後、短絡します。

0または0または1または0は0または0または1を評価し、0をドロップします


3

または、ラムダを避けてリストの代わりにジェネレータを使用したい場合:

(lstのcolに対してsorted(col)[1])


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]



1

sortまたはheapqを使用する代わりに、minおよびindexを使用してO(n)時間でそれを行うことができます。

最初に、元のリストの最小値を除くすべての新しいリストを作成します。

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

次に、新しいリストの最小値を取得します。

second_smallest = min(new_list)

これで、すべてが1つのラムダにまとめられました。

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関数が使用されるものです。

ではbind機能あなたは1つのラムダ、文を表す各ラムダに複数のラムダさんを組み合わせることができます。


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で内部
観察

0

はい。このように定義して、複数の式を次のようにラップすることができます。

スキーム開始:

begin = lambda * x:x [-1]

Common Lisp progn:

progn =ラムダ* x:x [-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
ソロモンUcko
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.