カレーにして


20

引数x 1x 2、…、x nをとる関数fを持つ

                                               –すなわち。  f:X 1 ×X 2 ×…×X n →Y

カリー化は、fを、さらに別の関数にマップする単一の引数a 1をとる関数として再定義します。このテクニックは、部分的に適用する場合に便利です。たとえば、カリー化したpow関数を使用して記述できますexp = pow(e)

3つの引数(f:X 1 ×X 2 ×X 3 →Y)を取る次の関数fがあると仮定します。

def f(a,b,c):
  return a + b * c

この関数をカリー化すると、f_curryが残ります:X 1 →(X 2 →(X 3 →Y))、その関数を2回呼び出すと、次と同等のf_curry(1)(2)関数(h)が返されます:

def h(c):
   return 1 + 2 * c

カリー化された関数fは次のように書くことができます(Python 3):

def f_curry(a):
  def g_curry(b):
    def h(c):
      return a + b * c
    return h
  return g_curry

オンラインでお試しください!

チャレンジ

あなたの挑戦は、上記の機能をカリー化することです。ルールは次のとおりです。

  • 入力は少なくとも2つの引数を取るブラックボックス関数になります
  • 入力関数には常に固定数の引数があります(printf類似または類似、注:任意の数の引数が2以上の関数をサポートする必要があります)
  • 言語がデフォルトでカリー化された関数(Haskellなど)を使用している場合、「高次関数」ではなく、N個のタプルで入力関数が定義されることが予想されます。
  • 入力として引数の数を取ることができます
  • 出力は、入力と同等のカリーになります*
  • 出力関数は次のようになると仮定できます。
    • 入力関数がとる引数の数以下で呼び出される
    • 適切なタイプの引数で呼び出されます

*これはfN引数を持つ入力hと、すべての有効な引数に対してa1,…,aNそれを保持する出力を意味しf(a1,a2,…,aN) == h(a1)(a2)…(aN)ます。



したがって、入力はdef f(a,b,c): return a + b * cあり、出力はdef f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curry
ダニエルインディー

@DanielIndie:その例を取り上げる場合、入力はf(どこかで定義されている)であり、出力はに相当するものでなければなりませんf_curry。または、入力はにlambda a,b,c: a+b*cなり、出力はに相当する関数になりf_curryます。
ბიმო

これは、ほとんどの静的に型付けされた言語では困難です...これには型関数が必要だと思います。
パウロEbermann

@PaŭloEbermann:確かに、一部の言語ではこのタスクを解決できません(機能プログラミングタグに注意してください)。ただし、一部の静的型付け言語では、有効なI / Oになる関数ポインターを使用できる場合があります。これが主に、引数の数を追加入力として許可した理由です。
21:22に

回答:



9

イドリス、204バイト

import Data.HVect
C:(a:Vect n Type)->(HVect a->Type)->Type
C[]T=T[]
C(h::t)T=(x:h)->C t(T .(x::))
c:{a:Vect n Type}->{T:HVect a->Type}->((b:HVect a)->T b)->C a T
c{a=[]}f=f[]
c{a=h::t}f=\v=>c(\u=>f(v::u))

オンラインでお試しください!

依存型の仕事のようですね!まあ、多分。


Cはカリー化タイプの関数です。型a = [t 1、t 2、…t n ]のベクトルと型関数T:HVect a→Typeが与えられると、新しい型を返します。

           (x 1  :t 1)→(x 2  :t 2)→…→(T [x 1、x 2、…x n ])

ここで、HVectは、イドリスプレリュードの異種ベクトルタイプです。要素がn個の異なるタイプであるnタプルのタイプです。

cは、暗黙的な引数としてaTを受け取りタイプ((b:HVect a)→T b)カリー化されていない関数fを、タイプC a Tのカリー化された関数に変換する関数です。

Cは単に私たちがやりたいことを説明します; cは実際にそれを行います。しかし、イドリスはすべてのトップレベルの定義に型シグネチャを要求するので、Cを定義しないことから逃れられません。)


TIOリンクは使用例を示します。次のように3タプル(Nat、Nat、String)の関数を定義する場合:

uncurried : HVect [Nat, Nat, String] -> String
uncurried [a, b, c] = show (a*a + b*b) ++ c

その後uncurried [3, 4, "th"]、と同じ結果が得られc uncurried 3 4 "th"ます。イドリスは議論a=[Nat, Nat, String]を推測し、T=const String私たちにとっては信じています。

このコードは、timjbのこの要点に基づいています。


私の意見では、HaskellとIdr​​isのタプルはHVectデフォルトで実際にあるはずHVectです-本質的にあなたはunconsできるタプルです。
エソランジングフルーツ


5

R、96バイト

y=function(f,n=length(formals(f)),p=list())function(x,u=c(p,x))`if`(n<2,do.call(f,u),y(f,n-1,u))

オンラインでお試しください!


以前のバージョン(97バイト)

@JayCEのおかげで-1バイト


基本的に短縮する方法がわかりません。最初の行の最後にあるブレースとスペースを取り除くことで、3バイト離れてゴルフをすることができます。また、ここでは、バイトカウントに関数の名前を含めないという慣習のため、さらに2つです。TIO
ngm

@ngm関数名は、再帰するときに含める必要があります。
Ørjanヨハンセン

@ngm:サブ関数内にifステートメントを挿入して、10分の1バイトを節約します:)
digEmAll


3

Python 2、60バイト

c=lambda f,n,l=[]:lambda a:n-1and c(f,n-1,l+[a])or f(*l+[a])

オンラインでお試しください!

フッターは、1行につき次の方法でSTDINを使用するテスターです。

  1. 関数自体
  2. 関数の引数の数、≥2
  3. 引数のリスト([a,b,...]

引数のリストはテスターで入力として提供されますが、実際には、カリー化された同等物がリストの先頭に追加され、リストは関数呼び出しによって削減されることに注意してください。

同様の55バイトバージョンがovsによって親切に提供されています。

c=lambda f,n,*l:lambda a:n-1and c(f,n-1,*l,a)or f(*l,a)

オンラインでお試しください!





1

アタッシュ、5バイト

Curry

オンラインでお試しください!

シンプルな組み込みで、ほとんど面白くない。しかし、ここにゼロからのバージョンがあります:

アタッシュ、35バイト

{If[#__2<#_,Fold[`&:,$'__],_@@__2]}

説明:

{If[#__2<#_,Fold[`&:,$'__],_@@__2]}
{                                 }    lambda
 If[       ,              ,      ]     if
    #__2                                 the number of parameters after the first
        <#_                              is less than the arity of the first
            Fold[   ,    ]             then fold:
                 `&:                     right-function bonding
                     $                     over this function
                      '__                  paired with the rest of the parameters
                          ,            otherwise:
                           _@@           call the first parameter
                              __2        with the rest of them

1

Java 8、46 + 318 = 364バイト

これはカリー化された(hah)ラムダであり、関数と引数カウントを取り、カリー化された関数を返します。

import java.lang.reflect.*;import java.util.*;

f->p->new java.util.function.Function(){Object F=f;Method m=F.getClass().getDeclaredMethods()[0];int P=p;List a=new Stack();public Object apply(Object r){a.add(r);try{return a.size()<P?this:m.invoke(F,a.toArray());}catch(Throwable t){t=t.getCause();if(t instanceof Error)throw(Error)t;else throw(RuntimeException)t;}}}

オンラインで試す

提出タイプ

入力機能

関数入力は、関数を表す単一のメソッド(継承されたメソッドを除く)を持つオブジェクトです。(たとえば)3つのパラメーターの機能をサポートする必要があるため、標準の機能インターフェイスを入力タイプとして使用できないことに注意してください。また、標準のjava.util.function.Functionような型にキャストされたラムダ式が渡される可能性があることに注意してください(単一のメソッドはですapply)。

チェックされた例外は入力関数で宣言できますが、スローされない場合があります(つまり、出力関数の呼び出し元に伝播されません)。これは、Javaの機能インターフェースがチェック済み例外を許可しないため受け入れられると推定されます(それらを伝搬すると、サブミットがを返さないようになりますFunction)。ランタイム例外(RuntimeExceptionまたはに割り当て可能Error)が伝播されます。

出力機能

提出の出力はjava.util.function.Function<Object, Object>です。メソッド(入力など)でプレーンObjectを返すことを検討applyしましたが、結果を呼び出すためにリフレクションが必要になりました。表現。

使用法

提出はObjectto から関数を返すので、Object出力は(withでapply)直接呼び出されますが、後続の中間戻り値は、java.util.function.Function<Object, Object>呼び出される前に適切な型(たとえば)にキャストする必要があります。使用例については、TIOを参照してください。

Javaの関数(メソッドなど)はファーストクラスのオブジェクトではないことに注意してください。したがって、チャレンジの説明の出力箇条書きで使用される構文は、Javaでは無意味です。f(a1, a2, a3)持っているよりも、持っているf.apply(a1, a2, a3)というよりf(a1)(a2)(a3)f.apply(a1).apply(a2).apply(a3)

制限事項

中間結果が適用される(引数が追加される)と、結果は実際には元の結果の変更されたコピーになります。たとえば、次のスニペットでは:

Function<Object, Function<Integer, Function>> submission = ...;
Function c = submission.apply((IntBinaryOperator) (a, b) -> a + b).apply(2);
Function c2 = (Function) c.apply(2);
System.out.println(c2.apply(2));
System.out.println(c2.apply(3));

行4は印刷されます4が、行5は失敗c2します。その時点で既に引数2andを保持しているためです2(これにも注意してくださいc2 == c)。これはカリー化の精神に違反しますが、課題で述べられた特定の要件を満たします。

非ゴルフ

コピーされていないコピーについては、TIOを参照してください。



弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.