web-dev-qa-db-fra.com

Une fonction lambda peut-elle s'appeler récursivement en Python?

Une fonction régulière peut contenir un appel à elle-même dans sa définition, pas de problème. Je n'arrive pas à comprendre comment le faire avec une fonction lambda bien que pour la simple raison que la fonction lambda n'a pas de nom auquel se référer. Y a-t-il un moyen de le faire? Comment?

61
dsimard

La seule façon pour moi d’y parvenir consiste à donner un nom à la fonction:

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

ou alternativement, pour les versions antérieures de python:

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

Mise à jour : en utilisant les idées des autres réponses, j'ai été en mesure de caler la fonction factorielle en un seul lambda sans nom:

>>> 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]

C'est donc possible, mais pas vraiment recommandé!

63
Greg Hewgill

sans réduire, mapper, nommer internes lambdas ou python:

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

Vous ne pouvez pas le faire directement, car il n’a pas de nom. Mais avec une fonction d'assistance telle que celle indiquée par Y-combinator, citée par Lemmy, vous pouvez créer une récursivité en lui passant la fonction comme paramètre (aussi étrange que cela puisse paraître):

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

Ceci affiche les dix premiers nombres de Fibonacci: [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

23
sth

Contrairement à ce que sth a dit, vous POUVEZ le faire directement.

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

La première partie est le combinateur à point fixeY qui facilite la récursivité dans le calcul lambda

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

la deuxième partie est la fonction factorielle fait définie récursivement

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

Y est appliqué à fact pour former une autre expression lambda

F = Y(fact)

qui est appliqué à la troisième partie, n, qui évoque à la nième factorielle

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

ou équivalent

>>> (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

Si toutefois vous préférez fibs à facts, vous pouvez le faire aussi en utilisant le même combinateur

>>> (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
22
Nux

Oui. J'ai deux façons de le faire, et une était déjà couverte. C'est ma manière préférée.

(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

Je n'ai jamais utilisé Python, mais this est probablement ce que vous recherchez.

6
Lemmy

Cette réponse est assez basique. C'est un peu plus simple que la réponse de Hugo Walter:

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

Réponse de Hugo Walter:

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

J'espère que cela serait utile à quelqu'un.

1
Sergey Luchko

Eh bien, pas exactement la récursion lambda pure, mais elle est applicable dans les endroits où vous ne pouvez utiliser que des lambdas, par exemple. réduire, mapper et lister les compréhensions, ou autres lambdas. L'astuce consiste à tirer profit de la compréhension des listes et de la portée du nom de Python. L'exemple suivant parcourt le dictionnaire par la chaîne de clés donnée.

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

Le lambda reprend son nom défini dans l'expression de compréhension de liste (fn). L'exemple est plutôt compliqué, mais il montre le concept.

0
ahanin

Pour cela, nous pouvons utiliser Des combinateurs à point fixe , en particulier un combinateur Z, car il fonctionnera dans des langages stricts, également appelés langages désirés:

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

Définissez la fonction fact et modifiez-la:

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

Remarquerez que:

fait === Z (_fact)

Et utilisez-le:

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

Voir aussi: Combinateurs à points fixes en JavaScript: Mémorisation des fonctions récursives

0
kogoia

Lambda peut facilement remplacer des fonctions récursives en Python:

Par exemple, ce compound_interest basique:

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

peut être remplacé par:

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

ou pour plus de visibilité:

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

USAGE:

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

Sortie:

13310.0
13310.0
0
Kevin Sabbe