web-dev-qa-db-ja.com

ラムダ関数はそれ自体をPythonで再帰的に呼び出すことができますか?

通常の関数は、定義にそれ自体への呼び出しを含めることができ、問題はありません。ラムダ関数に参照する名前がないという単純な理由から、ラムダ関数を使用してそれを行う方法を理解することはできません。それを行う方法はありますか?どうやって?

62
dsimard

これを行う唯一の方法は、関数に名前を付けることです。

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

または、Pythonの以前のバージョンの場合:

fact = lambda x: x == 0 and 1 or x * fact(x-1)

更新:他の答えのアイデアを使用して、階乗関数を単一の名前のないラムダにくさびで留めることができました:

>>> map(lambda n: (lambda f, *a: f(f, *a))(lambda rec, n: 1 if n == 0 else n*rec(rec, n-1), n), range(10))
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

可能ですが、実際にはお勧めしません!

68
Greg Hewgill

reduce、map、named lambdasまたはpython internals:

(lambda a:lambda v:a(a,v))(lambda s,x:1 if x==0 else x*s(s,x-1))(10)
47
Hugo Walter

Sthが言ったことに反して、これを直接行うことができます。

(lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(lambda f: (lambda i: 1 if (i == 0) else i * f(i - 1)))(n)

最初の部分は 固定小数点コンビネーター[〜#〜] y [〜#〜]で、ラムダ計算の再帰を容易にします

Y = (lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))

2番目の部分は、再帰的に定義された階乗関数factです

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

[〜#〜] y [〜#〜]factに適用され、別のラムダ式を形成します

F = Y(fact)

3番目の部分nに適用され、n番目の階乗を評価します

>>> n = 5
>>> F(n)
120

または同等に

>>> (lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(lambda f: (lambda i: 1 if (i == 0) else i * f(i - 1)))(5)
120

ただし、fibsfactsよりも好む場合は、同じコンビネータを使用してそれを行うこともできます

>>> (lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))(lambda f: (lambda i: f(i - 1) + f(i - 2) if i > 1 else 1))(5)
8
24
Nux

名前がないため、直接行うことはできません。しかし、YコンビネーターLemmyのようなヘルパー関数を使用すると、関数をパラメーターとしてそれ自体に渡すことで再帰を作成できます(奇妙に聞こえますが)。

# helper function
def recursive(f, *p, **kw):
   return f(f, *p, **kw)

def fib(n):
   # The rec parameter will be the lambda function itself
   return recursive((lambda rec, n: rec(rec, n-1) + rec(rec, n-2) if n>1 else 1), n)

# using map since we already started to do black functional programming magic
print map(fib, range(10))

これは、最初の10個のフィボナッチ数を出力します:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

23
sth

はい。それには2つの方法がありますが、1つはすでに説明しました。これは私の好みの方法です。

(lambda v: (lambda n: n * __import__('types').FunctionType(
        __import__('inspect').stack()[0][0].f_code, 
        dict(__import__=__import__, dict=dict)
    )(n - 1) if n > 1 else 1)(v))(5)
11
habnabit

Pythonを使用したことはありませんが、 this がおそらく探しているものです。

6
Lemmy

この答えはかなり基本的なものです。 Hugo Walterの答えよりも少し簡単です。

>>> (lambda f: f(f))(lambda f, i=0: (i < 10)and f(f, i + 1)or i)
10
>>>

ヒューゴ・ウォルターの答え:

(lambda a:lambda v:a(a,v))(lambda s,x:1 if x==0 else x*s(s,x-1))(10)
2
motoku
def recursive(def_fun):
    def wrapper(*p, **kw):
        fi = lambda *p, **kw: def_fun(fi, *p, **kw)
        return def_fun(fi, *p, **kw)

    return wrapper


factorial = recursive(lambda f, n: 1 if n < 2 else n * f(n - 1))
print(factorial(10))

fibonaci = recursive(lambda f, n: f(n - 1) + f(n - 2) if n > 1 else 1)
print(fibonaci(10))

それが誰かに役立つことを願っています。

1
Sergey Luchko

まあ、正確に純粋なラムダ再帰ではありませんが、ラムダのみを使用できる場所で適用できます。内包表記、またはその他のラムダを削減、マップ、およびリストします。秘Theは、リストの理解とPythonの名前スコープの恩恵を受けることです。次の例では、指定されたキーのチェーンで辞書を走査します。

>>> data = {'John': {'age': 33}, 'Kate': {'age': 32}}
>>> [fn(data, ['John', 'age']) for fn in [lambda d, keys: None if d is None or type(d) is not dict or len(keys) < 1 or keys[0] not in d else (d[keys[0]] if len(keys) == 1 else fn(d[keys[0]], keys[1:]))]][0]
33

ラムダは、リスト内包表記(fn)で定義された名前を再利用します。この例はかなり複雑ですが、概念を示しています。

0
ahanin

これには Fixed-point combinators 、具体的にはZコンビネーターを使用できます。これは、熱心な言語とも呼ばれる厳格な言語で動作するためです。

const Z = f => (x => f(v => x(x)(v)))(x => f(v => x(x)(v)))

fact関数を定義して変更します。

1. const fact n = n === 0 ? 1 : n * fact(n - 1)
2. const fact = n => n === 0 ? 1 : n * fact(n - 1)
3. const _fact = (fact => n => n === 0 ? 1 : n * fact(n - 1))

次のことに注意してください。

事実=== Z(_fact)

そしてそれを使用します:

const Z = f => (x => f(v => x(x)(v)))(x => f(v => x(x)(v)));

const _fact = f => n => n === 0 ? 1 : n * f(n - 1);
const fact = Z(_fact);

console.log(fact(5)); //120

参照: JavaScriptの固定小数点コンビネータ:再帰関数のメモ

0
kogoia

Lambdaは、Pythonの再帰関数を簡単に置き換えることができます。

たとえば、次の基本的なcompound_interest:

def interest(amount, rate, period):
    if period == 0: 
        return amount
    else:
        return interest(amount * rate, rate, period - 1)

次のものに置き換えることができます。

lambda_interest = lambda a,r,p: a if p == 0 else lambda_interest(a * r, r, p - 1)

またはより見やすくするために:

lambda_interest = lambda amount, rate, period: \
amount if period == 0 else \
lambda_interest(amount * rate, rate, period - 1)

使用法:

print(interest(10000, 1.1, 3))
print(lambda_interest(10000, 1.1, 3))

出力:

13310.0
13310.0
0
Kevin Sabbe