web-dev-qa-db-ja.com

カレーと可変機能を同時に持つことは可能ですか?

動的型付けの関数型プログラミング言語でカリー関数と可変長関数の両方を使用できるようにすることを考えていますが、それが可能かどうかは疑問です。

ここにいくつかの擬似コードがあります:

sum = if @args.empty then 0 else @args.head + sum @args.tail

これはおそらくそのすべての引数を合計することです。次に、sum自体が数値として扱われる場合、結果は0になります。例えば、

sum + 1

+が数値でのみ機能すると想定すると、1になります。ただし、sum == 0がtrueの場合でも、sumは、指定された引数の数に関係なく、その値と機能プロパティを維持します(したがって、「部分的に適用される」と「可変」が同時に)。 、私が宣言した場合

g = sum 1 2 3

その場合、g6と等しくなりますが、gをさらに適用することもできます。たとえば、g 4 5 == 15はtrueです。この場合、オブジェクトgをリテラル6で置き換えることはできません。整数として扱うと同じ値が得られますが、内部に異なるコードが含まれているためです。

この設計を実際のプログラミング言語で使用すると、混乱や曖昧さが生じますか?

13
Michael Tsang

可変引数はどのように実装できますか?引数リストの終わりを知らせる何らかのメカニズムが必要です。これは

  • 特別なターミネーター値、または
  • 追加パラメーターとして渡される可変引数リストの長さ。

これらのメカニズムはどちらも、カリー化のコンテキストで可変引数を実装するために使用できますが、適切なタイピングが大きな問題になります。この関数がカリー化を使用していることを除いて、関数_sum: ...int -> int_を扱っていると仮定しましょう(したがって、実際には、引数の数がわからないことを除いて、_sum: int -> ... -> int -> int_のような型があります)。

ケース:ターミネーター値:endを特別なターミネーターとし、Tsumのタイプとします。これで、endに適用すると、関数は_sum: end -> int_を返し、intに適用すると、別の合計のような関数__sum: int -> T_が得られます。したがって、Tは次のタイプの結合です:T = (end -> int) | (int -> T)Tに置き換えると、_end -> int_、_int -> end -> int_、_int -> int -> end -> int_などのさまざまな型が得られます。ただし、ほとんどの型システムはそのような型に対応していません。

ケース:明示的な長さ:可変引数関数の最初の引数は可変引数の数です。したがって、_sum 0 : int_、_sum 1 : int -> int_、_sum 3 : int -> int -> int -> int_などです。これは、一部の型システムでサポートされており、依存型の例です。実際には、引数の数は型パラメーターであり、通常のパラメーターではありません–関数のアリティがランタイム値に依存することは意味がありません。s = ((sum (floor (Rand 3))) 1) 2は明らかに型が間違っています:これは評価されますs = ((sum 0) 1) 2 = (0 1) 2s = ((sum 1) 1) 2 = 1 2、またはs = ((sum 2) 1) 2 = 3のいずれかに。

実際には、エラーが発生しやすく、一般的な型システムに(意味のある)型がないため、これらの手法は使用しないでください。代わりに、値のリストを1つのパラメーターとして渡すだけです:_sum: [int] -> int_。

はい、オブジェクトが関数と値の両方として表示される可能性があります。強制型システムで。 sumSumObjとします。これには2つの強制があります。

  • _coerce: SumObj -> int -> SumObj_は、sumを関数として使用できるようにします。
  • _coerce: SumObj -> int_を使用すると、結果を抽出できます。

技術的には、これは上記のターミネーター値のケースのバリエーションであり、_T = SumObj_、およびcoerceはタイプのアンラッパーです。多くのオブジェクト指向言語では、これは演算子のオーバーロードで簡単に実装できます。 C++:

_#include <iostream>
using namespace std;

class sum {
  int value;
public:
  explicit sum() : sum(0) {}
  explicit sum(int x) : value(x) {}
  sum operator()(int x) const { return sum(value + x); }  // function call overload
  operator int() const { return value; } // integer cast overload
};

int main() {
  int zero = sum();
  cout << "zero sum as int: " << zero << '\n';
  int someSum = sum(1)(2)(4);
  cout << "some sum as int: " << someSum << '\n';
}
_
6
amon

Haskellでのprintfのこの実装 を、 この動作の説明 とともに確認することをお勧めします。後者のページには、このようなことを行うことに関するOleg Kiselyovの論文へのリンクがあり、これも一読の価値があります。実際、関数型言語を設計している場合は、 OlegのWebサイト を強制的に読む必要があります。

私の意見では、これらのアプローチは少しハックですが、それが可能であることを示しています。ただし、言語が完全に依存する型付けを備えている場合は、はるかに簡単です。その整数引数を合計する可変関数は次のようになります。

type SumType = (t : union{Int,Null}) -> {SumType, if t is Int|
                                         Int,     if t is Null}
sum :: SumType
sum (v : Int) = v + sum
sum (v : Null) = 0

明示的な名前を付ける必要なしに再帰型を定義するための抽象化により、そのような関数の記述が容易になる場合があります。

編集:もちろん、私はもう一度質問を読んだだけで、動的に型付けされた言語を言った、その時点で明らかに型力学は本当に関連していない、したがって、@ amonの回答にはおそらく必要なものがすべて含まれています。ええと、静的言語でどうしたらいいのか疑問に思っている人がいるので、ここに置いておきます...

2
Jules

以下は、Pythonのオプションの引数を利用して、@ amonの「ターミネーター」アプローチを使用するPython3の可変個関数のカリー化バージョンです。

def curry_vargs(g):
    actual_args = []
    def f(a, force=False):
        nonlocal actual_args
        actual_args.append(a)
        if force:
            res = g(*actual_args)
            actual_args = []
            return res
        else:
            return f
    return f

def g(*args): return sum(args)
f = curry_vargs(g)
f(1)(2)(3)(4,True) # => 10

返される関数fは、外部スコープにバインドされている配列内の連続した呼び出しで渡された引数を収集します。 force引数がtrueの場合のみ、これまでに収集されたすべての引数を使用して元の関数が呼び出されます。

この実装の注意点は、常に最初の引数をfに渡す必要があるため、すべての引数がバインドされ、空の引数リストでのみ呼び出すことができる「サンク」関数を作成できないことです(ただし、私はこれはカレーの典型的な実装と一致していると思います)。

もう1つの注意点は、間違った引数(たとえば、間違った型)を渡すと、元の関数を再度カリー化する必要があることです。内部配列をリセットする他の方法はありません。これは、カリー化された関数が正常に実行された後にのみ行われます。

「オブジェクトを関数と非関数の値にすることはできますか?」という簡単な質問は、かっこなしの関数への参照が内部の関数オブジェクトとして評価されるため、Pythonで実装できますか? 。これを曲げて任意の値を返すことができるかどうかはわかりません。

LISPシンボルは値と関数値を同時に持つことができるため、LISPではおそらく簡単です。シンボルが関数の位置に(リストの最初の要素として)出現すると、関数の値が選択されます。

0
ThomasH