Python körili ve kısmi

Aug 19 2020

Codewars.com'da programlama alıştırmaları yaparken, körleme ve kısmi fonksiyonlar üzerine bir alıştırma ile karşılaştım .

Programlamada acemi ve konuyla ilgili yeni biri olarak, konuyla ilgili bilgileri internette araştırdım ve alıştırmayı çözme konusunda epey ilerledim. Ancak şimdi üstesinden gelemediğim bir engelle karşılaştım ve burada doğru yönde bir dürtü arıyorum.

Alıştırma oldukça basittir: herhangi bir girdi işlevini körükleyebilen ve / veya parçalayabilen ve yeterli girdi parametresi sağlandığında girdi işlevini değerlendiren bir işlev yazın. Giriş işlevi, herhangi bir sayıda giriş parametresini kabul edebilir. Ayrıca köri / kısmi işlev, çağrılma şekli açısından çok esnek olmalı, işlevi çağırmanın birçok farklı yolunu kullanabilmelidir. Ayrıca, curry / kısmi işlevin, girdi işlevinin gerektirdiğinden daha fazla girdiyle çağrılmasına izin verilir, bu durumda tüm fazla girdilerin göz ardı edilmesi gerekir.

Alıştırma bağlantısının ardından, tüm test durumlarının işlevin üstesinden gelebilmesi gerektiği bulunabilir.

Bulduğum kod şu:

from functools import partial
from inspect import signature

def curry_partial(func, *initial_args):
    """ Generates a 'curried' version of a function. """

    # Process any initial arguments that where given. If the number of arguments that are given exceeds 
    # minArgs (the number of input arguments that func needs), func is evaluated

    minArgs = len(signature(func).parameters)
    if initial_args:
        if len(initial_args) >= minArgs: 
            return func(*initial_args[:minArgs])

        func = partial(func, *initial_args)
        minArgs = len(signature(func).parameters)

    
    # Do the currying
    def g(*myArgs):
        nonlocal minArgs

        # Evaluate function if we have the necessary amount of input arguments
        if minArgs is not None and minArgs <= len(myArgs):
                return func(*myArgs[:minArgs]) 
            
        def f(*args):
            nonlocal minArgs
            newArgs = myArgs + args if args else myArgs

            if minArgs is not None and minArgs <= len(newArgs):
                return func(*newArgs[:minArgs])
            else:
                return g(*newArgs)  
        return f
    return g

Şimdi, aşağıdaki test yürütüldüğünde bu kod başarısız olur:

test.assert_equals(curry_partial(curry_partial(curry_partial(add, a), b), c), sum)

burada topla = a + b + c (doğru tanımlanmış işlev), a = 1, b = 2, c = 3 ve toplam = 6.

Bunun başarısız olmasının nedeni, curry_partial(add, a)işleve bir işlev tutamacı döndürmesidir g. İkinci çağrıda, curry_partial(<function_handle to g>, b)hesaplama minArgs = len(signature(func).parameters)benim istediğim gibi çalışmıyor, çünkü şimdi fonksiyonun kaç tane girdi argümanı ggerektirdiğini ( 1yani: ie *myArgs) ve orijinalin funchala kaç tane gerektirdiğini hesaplayacak . Öyleyse soru şu ki, kodumu nasıl yazabilirim ki, orijinalimin funchala kaç girdi argümanına ihtiyaç duyduğunu takip edebilirim (fonksiyonu herhangi bir ilk argümanla her ayırdığımda bu sayıyı azaltarak).

Programlama ve körleme / kısmi hakkında öğrenecek çok şeyim var, bu yüzden büyük olasılıkla en uygun yaklaşımı seçmedim. Ama öğrenmek isterim. Benim için bu alıştırmadaki zorluk, kısmi ve köri kombinasyonudur, yani karşılaşılan herhangi bir ilk argümanı paylaşırken bir curry döngüsü yapmak.

Yanıtlar

1 Ava Aug 20 2020 at 13:27

Bunu deneyin.

from inspect import signature

# Here `is_set` acts like a flip-flop
is_set = False
params = 0

def curry_partial(func, *partial_args):
    """
    Required argument: func
    Optional argument: partial_args
    Return:
        1) Result of the `func` if
           `partial_args` contains
           required number of items.
        2) Function `wrapper` if `partial_args`
           contains less than the required
           number of items.
    """

    global is_set, params
    
    if not is_set:
        is_set = True
        
        # if func is already a value
        # we should return it
        try: params = len(signature(func).parameters)
        except: return func
    
    try:
        is_set = False
        return func(*partial_args[:params])
    
    except:
        is_set = True
    
        def wrapper(*extra_args):
            """
            Optional argument: extra_args
            Return:
                1) Result of the `func` if `args`
                   contains required number of
                   items.
                2) Result of `curry_partial` if
                   `args` contains less than the
                   required number of items.
            """
            
            args = (partial_args + extra_args)
            
            try:
                is_set = False
                return func(*args[:params])
            except:
                is_set = True
                return curry_partial(func, *args)
    
    return wrapper

Bu gerçekten de tasarım açısından pek iyi değil. Bunun yerine class, örneğin flip-flop gibi tüm dahili işleri yapmak için kullanmalısınız (endişelenmeyin, orada herhangi bir flip-flop'a ihtiyacımız yok ;-)).

Her ne zaman rastgele argümanlar alan bir işlev varsa, bu sınıfı her zaman işlevi geçirerek başlatabilirsiniz. Ama bu sefer bunu sana bırakıyorum.

Nishant Aug 21 2020 at 02:46

Körleme konusunda emin değilim , ancak basit bir kısmi işlev oluşturucuya ihtiyacınız varsa, şöyle bir şey deneyebilirsiniz:

from functools import partial
from inspect import signature

def execute_or_partial(f, *args):
    max = len(signature(f).parameters)
    if len(args) >= max: 
        return f(*args[:max])
    else:
        return partial(f, *args)

s = lambda x, y, z: x + y + z

t = execute_or_partial(s, 1)
u = execute_or_partial(t, 2)
v = execute_or_partial(u, 3)

print(v)

or

print(execute_or_partial(execute_or_partial(execute_or_partial(s, 1), 2), 3))

Orijinal probleminizi çözmese bile, yukarıdaki kodu kod tekrarını azaltmak için kullanıp kullanamayacağınıza bakın (Emin değilim, ancak iç fonksiyonda bazı kod tekrarları olduğunu düşünüyorum?); bu, sonraki sorunların çözülmesini kolaylaştıracaktır.

Standart kitaplıkta bu sorunu çözen işlevler olabilir. Haskell gibi birçok saf işlevsel dil, bu özelliğe sahiptir.