関数型プログラミングの「部分関数」とは、正確にはどういう意味ですか?


55

私の理解によると、部分関数は、関数に渡すパラメーターが予想よりも少ないことで得られる関数です。たとえば、これがPythonで直接有効だった場合:

>>> def add(x,y):
...    return x+y
... 
>>> new_function = add(1)
>>> new_function(2)
3

上記のスニペットでnew_functionは、部分的な関数です。ただし、Haskell Wikiによれば、部分関数の定義は

部分関数は、指定されたタイプのすべての可能な引数に対して定義されていない関数です。

だから、私の質問は、「部分的な機能」とは正確には何を意味するのですか?


37
部分的に適用された関数を部分的な関数と混同しています。
Willem Van Onsem

11
Python partial部分適用を実行しますが、Haskellはそれを自動的に実行します。wikiエントリは、数学の用語である部分関数を参照しています。
L3viathan

9
厳密に言うと、Haskellは部分的な関数の適用を行いません。すべての関数は1つの引数を取り、関数アプリケーションは関数を1つの引数に適用します。カリー化は、最初に複数の引数を持つ関数をシミュレートすることにより、別の言語での部分的なアプリケーションと考えるものをシミュレートします。のようなものadd 3 5は、単一の機能のアプリケーションではありません。これは、最初に適用されadd、その後5に適用される新機能、取得するには3
chepner

C#では、partialメソッドは、プロジェクトのコードベースの他の場所にある、オプションで実装されたプライベートメソッドの前方宣言です。

1
あなたの例を有効にすることができます:new_function = functools.partial(add, 1)
wjandrea

回答:


76

ここでは2つの概念が混乱しています。部分的に適用される関数 [ハスケル-ウィキ]を有する一部の機能は、 [ハスケル-ウィキ]

部分的に適用される関数は次のとおりです。

Haskellでの部分的なアプリケーションでは、複数の引数を取る関数に引数の総数より少ない数を渡す必要があります。

一方、部分関数は実際には非合計関数です。

部分関数は、指定されたタイプのすべての可能な引数に対して定義されていない関数です。


24
これは良い答えですが、部分的な関数の例を答え自体に追加することで改善できます。
への接近

2
部分的に適用された関数の正確な定義に同意するかどうかはわかりません。Haskellの関数は常に1つの引数のみを取り、「複数の引数」を取りません。「Haskellでの部分的なアプリケーション(部分的なアプリケーションの適用)には、別の引数にさらに適用できない値を取得するために必要な引数の総数よりも少ない数を指定する必要がある」という定義を使用します。(ここから改作)
TerryA

21

部分的な関数(関数型プログラミングと数学の両方のコンテキストで)は、まさにwikiが言っていることです。可能な引数のすべてに対して定義されていない関数です。プログラミングのコンテキストでは、「未定義」は通常、未定義の動作、例外、非終了など、いくつかのことの1つとして解釈されます。

部分関数の例は整数除算であり、除数が0の場合は定義されません(Haskellではエラーがスローされます)。

上記のスニペットでは、new_functionは部分的な関数です。

そのコードはPythonでエラーを引き起こすだけですが、意図したとおりに機能した場合、それは完全な(部分的ではない)関数になります。

コメンターがすでに指摘したように、あなたはそれが部分的に適用された関数であるという事実を考えている可能性が最も高いです。


18

答えはすべてを説明しています、私は各言語で1つの例を追加します:

def add(x,y):
    return x+y

f = add(1)
print(f(3))

    f = add(1)
TypeError: add() missing 1 required positional argument: 'y'

これは部分関数でもカレー関数もありません。これは、引数をすべて指定しなかった関数にすぎません

Pythonのカレー関数は次のようになります。

partialAdd= lambda x: lambda y: x + y

plusOne = partialAdd(1)
print(plusOne(3))

4

そしてhaskellで:

plus :: Int -> Int -> Int
plus x y = x + y

plusOne = plus 1

plusOne 4

5

Pythonの部分関数:

def first(ls):
    return ls[0]

print(first([2,4,5]))
print(first([]))

出力

2

print(first([]))
  File "main.py", line 2, in first
    return ls[0]
IndexError: list index out of range

そしてHaskellでは、あなたのリンクが現れたように:

head [1,2,3]
3

head []
*** Exception: Prelude.head: empty list

では、総関数とは何でしょうか?

まあ、基本的には逆です。これは、そのタイプの任意の入力に対して機能する関数です。これがPythonの例です:

def addElem(xs, x):
  xs.append(x)
  return xs

少しトリックを使えば、これは無限リストでも機能します:

def infiniList():
    count = 0
    ls = []
    while True:
        yield ls
        count += 1
        ls.append(count)

ls = infiniList()
for i in range(5):
  rs = next(ls)

print(rs, addElem(rs,6))

[1, 2, 3, 4]
[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]

そしてHaskellの同等のもの:

addElem :: a -> [a] -> [a]
addElem x xs = x : xs

addElem 3 (take 10 [1..])
=> [3,1,2,3,4,5,6,7,8,9,10]

ここで、関数は永久にハングしません。概念は同じです。すべてのリストで関数が機能します。


pythonが標準ライブラリの部分関数をサポートしていることは言及する価値があります。
モニカ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.