簡體   English   中英

Python 局部套用

[英]Python currying and partial

在 codewars.com 上做編程練習時,我遇到了一個關於柯里化和偏函數的練習

作為編程新手和該主題的新手,我在 inte.net 上搜索了有關該主題的信息,並深入了解如何解決該練習。 然而,我現在偶然發現了一個我似乎無法克服的障礙,我在這里尋找正確方向的推動力。

練習相當簡單:編寫一個 function,它可以柯里化和/或部分輸入 function,並在提供足夠的輸入參數后評估輸入 function。 輸入 function 可以接受任意數量的輸入參數。 此外,curry/partial function 在調用方式上應該非常靈活,能夠處理調用 function 的許多不同方式。此外,curry/partial function 允許使用比輸入所需更多的輸入來調用function,在這種情況下,所有多余的輸入都需要忽略。

跟隨練習鏈接,可以找到function需要能夠處理的所有測試用例。

我想出的代碼如下:

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

現在,當執行以下測試時,此代碼失敗:

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

其中 add = a + b + c(正確定義的函數),a = 1,b = 2,c = 3,sum = 6。

失敗的原因是因為curry_partial(add, a)將 function 句柄返回到 function g 在第二次調用中, curry_partial(<function_handle to g>, b) ,計算minArgs = len(signature(func).parameters)不像我想要的那樣工作,因為它現在將計算多少輸入 arguments function g需要(即1 :即*myArgs ),而不是原始func仍然需要多少。 所以問題是,我如何編寫我的代碼,以便我可以跟蹤我的原始func仍然需要多少輸入 arguments(每次我使用任何給定的初始參數對 function 進行分部時減少該數字)。

關於編程和柯里化/部分,我還有很多東西要學習,所以很可能我沒有選擇最方便的方法。 但我想學習。 對我來說,這個練習的難點在於 partial 和 curry 的組合,即在對遇到的任何初始 arguments 進行 partially 時執行 curry 循環。

試試這個。

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

這確實不是很好的設計。 相反,您應該使用class來完成所有內部工作,例如觸發器(別擔心,我們那里不需要任何觸發器;-))。

每當有一個 function 接受任意 arguments 時,您總是可以實例化 class 傳遞 function。但是這次,我把它留給你。

我不確定currying ,但如果你需要一個簡單的部分 function 生成器,你可以嘗試這樣的事情:

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

就算不能解決你原來的問題,看看你能不能用上面的代碼來減少代碼重復(我不確定,但我覺得里面function有一些代碼重復?); 這將使后續問題更容易解決。

標准庫中可能有一些函數已經解決了這個問題。 許多純函數式語言,如 Haskell,都在語言中內置了此功能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM