8

Pythonの学習を始めたばかりです。ラムダ関数に出くわしました。問題の 1 つで、著者は階乗用のワンライナー ラムダ関数を書くように依頼しました。

これは与えられた解決策です:

num = 5
print (lambda b: (lambda a, b: a(a, b))(lambda a, b: b*a(a, b-1) if b > 0 else 1,b))(num)

奇妙な構文が理解できません。a(a,b) とはどういう意味ですか?

誰か説明できますか?

ありがとう

4

11 に答える 11

8

階乗自体は、ほぼ期待どおりです。aあなたはそれが... 階乗関数であると推測します。bは実パラメータです。

<factorial> = lambda a, b: b*a(a, b-1) if b > 0 else 1

このビットは、階乗の適用です。

<factorial-application> = (lambda a, b: a(a, b))(<factorial>, b)

a階乗関数そのものです。それ自体を最初の引数とし、評価点を 2 番目の引数とします。これは、気にしない限り次のように一般化できrecursive_lambdaます。a(a, b - 1)a(b - 1)

recursive_lambda = (lambda func: lambda *args: func(func, *args))
print(recursive_lambda(lambda self, x: x * self(self, x - 1) if x > 0 else 1)(6))
# Or, using the function verbatim:
print(recursive_lambda(lambda a, b: b*a(a, b-1) if b > 0 else 1)(6))

したがって、外側の部分があります。

(lambda b: <factorial-application>)(num)

ご覧のとおり、呼び出し元が渡さなければならないのは評価ポイントだけです。


実際に再帰ラムダが必要な場合は、ラムダに次のように名前を付けることができます。

fact = lambda x: 1 if x == 0 else x * fact(x-1)

そうでない場合は、単純なヘルパー関数を使用できます。retラムダが自身を参照できなかった前のコードとは異なり、これは自身を参照できるラムダであることがわかります。

def recursive_lambda(func):
    def ret(*args):
        return func(ret, *args)
    return ret

print(recursive_lambda(lambda factorial, x: x * factorial(x - 1) if x > 1 else 1)(6))  # 720

どちらの方法でも、ラムダをそれ自体に渡すばかげた手段に頼る必要はありません。

于 2013-03-14T04:48:33.380 に答える
6

この 1 つのライナーをタマネギのように剥がしてみましょう。

print (lambda b: (Y))(num)

無名関数を作成し (キーワード lambda は、一連のパラメーター名、コロン、それらのパラメーターを使用する関数を入力しようとしていることを意味します)、その 1 つのパラメーターを満たすために num を渡します。

   (lambda a, b: a(a, b))(X,b)

ラムダの内部で、別のラムダを定義します。このラムダを Y と呼びます。これは、a と b の 2 つのパラメーターを取ります。a は a と b で呼び出されるため、a はそれ自体ともう 1 つのパラメーターを取る呼び出し可能オブジェクトです。

            (lambda a, b: b*a(a, b-1) if b > 0 else 1
            ,
            b)

これらは Y へのパラメーターです。最初のものはラムダ関数で、X と呼びます。X が階乗関数であり、2 番目のパラメーターがその数値になることがわかります。

つまり、上に行って Y を見ると、次のように呼び出すことがわかります。

X(X, b)

どちらがしますか

b*X(X, b-1) if b > 0 else 1

そして自分自身を呼び出し、factorial の再帰部分を形成します。

外側をずっと見てみると、b は最も外側のラムダに渡した num であることがわかります。

num*X(X, b-1) if num > 0 else 1

これは紛らわしいワンライナーとして書かれたので、ちょっと紛らわしいです:)

于 2013-03-14T04:52:27.190 に答える
2

この関数には 2 つの難しい部分があります。
1. lambda a, b: b*a(a, b-1) if b > 0 else 1.
2. 1 の後に続く「b」。

1 の場合は、次のとおりです。

def f(a, b):
    if b > 0:
        b * a(a, b - 1)
    else:
        1

2 の場合、この b

(lambda b: (lambda a, b: a(a, b))(lambda a, b: b*a(a, b-1) if b > 0 else 1,b))(num)
                                                                      (this one)

実際にこれはbです:

(lambda b: (lambda a, b: a(a, b))(lambda a, b: b*a(a, b-1) if b > 0 else 1,b))(num)
   (this one)

その理由は、2 番目と 3 番目のラムダの定義内にないため、最初の b を参照するためです。

num を適用して外側の関数を取り除いた後:

(lambda a, b: a(a, b))  (lambda a, b: b*a(a, b-1) if b > 0 else 1, num) 

タプルに関数を適用するだけです (lambda a, b: b*a(a, b-1) if b > 0 else 1, num)
このタプルを (f, num) と呼びましょう (f の定義は上にあります)それに適用lambda a, b: a(a, b)すると、取得します

f(f, 数値)。

num が 5 であると仮定します
。f の定義により、最初に評価されます。

5 * f(f, 4)  

次に:

5 * (4 * f(f, 3)) 

までずっと

5 * (4 * (3 * (2 * (1 * f(f, 0)))))

f(f, 0) は 1 になります。

5 * (4 * (3 * (2 * (1 * 1))))

では、5 の階乗です。

于 2013-03-14T05:54:37.863 に答える
0

再帰ラムダの一般化された定義は次のとおりです

recursive_lambda = (lambda func: lambda *args: func(func, *args))
于 2016-06-06T06:09:36.017 に答える