12

非常にまれに、無名関数を返す無名関数を使用するPythonのコードに出くわすことはありません...?

残念ながら、手元に例は見つかりませんが、通常は次のような形式になります。

g = lambda x,c: x**c lambda c: c+1

なぜ誰かがこれをするのでしょうか?たぶんあなたは意味のある例をあげることができます(私が作ったものが意味をなすかどうかはわかりません)。

編集:ここに例があります:

swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])),
       f(y,a[x][0]),f(x,a[x][1])))()
4

8 に答える 8

18

あなたはカリー化をするためにそのような構造を使うことができます:

curry = lambda f, a: lambda x: f(a, x)

あなたはそれを次のように使うかもしれません:

>>> add = lambda x, y: x + y
>>> add5 = curry(add, 5)
>>> add5(3)
8
于 2010-12-06T00:17:39.383 に答える
4
swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])),
       f(y,a[x][0]),f(x,a[x][1])))()

最後に () が見えますか? 内部ラムダは返されず、呼び出されます。

関数は次と同等のことを行います

def swap(a, x, y):
     a[x] = (a[x], a[y])
     a[y] = a[x][0]
     a[x] = a[x][1]

しかし、ラムダでこれを行いたいとしましょう。ラムダでは割り当てを使用できません。ただし、__setitem__同じ効果を求めることができます。

def swap(a, x, y):
     a.__setitem__(x, (a[x], a[y]))
     a.__setitem__(y, a[x][0])
     a.__setitem__(x, a[x][1])

しかし、ラムダの場合、式は 1 つしか持てません。しかし、これらは関数呼び出しなので、タプルにまとめることができます

def swap(a, x, y):
     (a.__setitem__(x, (a[x], a[y])),
     a.__setitem__(y, a[x][0]),
     a.__setitem__(x, a[x][1]))

ただし、これらすべての__setitem__'s は私を失望させているので、それらを因数分解しましょう。

def swap(a, x, y):
     f = a.__setitem__
     (f(x, (a[x], a[y])),
     f(y, a[x][0]),
     f(x, a[x][1]))

ダグナミット、別の任務を追加することはできません! デフォルトのパラメーターを悪用しましょう。

def swap(a, x, y):
     def inner(f = a.__setitem__):
         (f(x, (a[x], a[y])),
         f(y, a[x][0]),
         f(x, a[x][1]))
     inner()

では、ラムダに切り替えましょう。

swap = lambda a, x, y: lambda f = a.__setitem__: (f(x, (a[x], a[y])), f(y, a[x][0]),  f(x, a[x][1]))()

元の表現に戻ります (プラス/マイナスのタイプミス)

これらはすべて、 「なぜ?」という疑問につながります。

関数は次のように実装されている必要があります

def swap(a, x, y):
    a[x],a[y] = a[y],a[x]

元の作成者は、関数ではなくラムダを使用するために道を踏み外しました。なんらかの理由で、ネストされた関数が気に入らない可能性があります。知らない。私が言うのは、その悪いコードです。(不可解な理由がある場合を除きます。)

于 2010-12-06T00:42:08.650 に答える
2

一時的なプレースホルダーに役立ちます。デコレータファクトリがあるとします。

@call_logger(log_arguments=True, log_return=False)
def f(a, b):
    pass

一時的に交換できます

call_logger = lambda *a, **kw: lambda f: f

間接的にラムダを返す場合にも役立ちます。

import collections
collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(int)))

Pythonコンソールで呼び出し可能なファクトリを作成する場合にも役立ちます。

そして、何かが可能であるという理由だけで、あなたがそれを使わなければならないという意味ではありません。

于 2010-12-06T00:18:28.260 に答える
2

先日、ユニットテストスイートのテストメソッドを無効にするために、このようなことをしました。

disable = lambda fn : lambda *args, **kwargs: None

@disable
test_method(self):
    ... test code that I wanted to disable ...

後で簡単に再度有効にできます。

于 2010-12-06T00:39:01.963 に答える
1

これを使用して、いくつかの一般的な反復コードを引き出すことができます (もちろん、Python でこれを実現する方法は他にもあります)。

おそらくあなたはロガーを書いていて、ログ文字列の前にレベルを追加する必要があります。次のように書くかもしれません:

import sys
prefixer = lambda prefix: lambda message: sys.stderr.write(prefix + ":" + message + "\n")
log_error = prefixer("ERROR")
log_warning = prefixer("WARNING")
log_info = prefixer("INFO")
log_debug = prefixer("DEBUG")

log_info("An informative message")
log_error("Oh no, a fatal problem")

このプログラムは出力します

   INFO:An informative message
   ERROR:Oh no, a fatal problem
于 2010-12-06T00:22:06.817 に答える
0

これを使用して、より継続的/トランポリン スタイルのプログラミングを実現できます。

継続渡しスタイルを参照してください

于 2010-12-06T01:09:18.963 に答える
0

ほとんどの場合、少なくとも私が遭遇し、私自身が書いたコードでは、ラムダ関数が作成された時点での値で変数を「フリーズ」するために使用されます。そうしないと、非ローカル変数が存在するスコープ内の変数を参照するため、望ましくない結果が生じることがあります。

たとえば、それぞれが 0 から 9 までのスカラーの乗数である 10 個の関数のリストを作成したい場合、次のように書きたくなるかもしれません。

>>> a = [(lambda j: i * j) for i in range(10)]
>>> a[9](10)
90

誰でも、他のファクトリ化された関数を使用したい場合は、同じ結果が得られます。

>>> a[1](10)
90

これは、ラムダが作成されたときに、ラムダ内の「i」変数が解決されないためです。むしろ、Python は "for" ステートメントの "i" への参照を、それが作成されたスコープで保持します (この参照は、ラムダ関数クロージャーで保持されます)。ラムダが実行されると、変数が評価され、その値がそのスコープ内の最後の値になります。

次のように 2 つのネストされたラムダを使用する場合:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)]

「i」変数は、「for」ループの実行中に評価されます。この値は "k" に渡されます。"k" は、因数分解する乗数関数の非ローカル変数として使用されます。i の値ごとに、囲んでいるラムダ関数の異なるインスタンスと、「k」変数の異なる値が存在します。

したがって、元の意図を達成することが可能です:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)]
>>> a[1](10)
10
>>> a[9](10)
90
>>>
于 2010-12-06T00:44:49.350 に答える