2017-01-13 2 views
1

私はカリングコンセプト、または少なくともSCALAカレー表記を理解するのに問題が生じています。Scalaでのカリングの理解

wikipediaは、複数の引数をとる関数の評価を1つの引数を持つ関数のシーケンスの評価に変換する技法であることを示しています。

この説明に続いて、次の2行はスカラーについて同じですか?

def addCurr(a: String)(b: String): String = {a + " " + b} 
def add(a:String): String => String = {b => a + " " + b} 

(addCurrについて考える私の方法を、私は同じ結果を得ることと同じ文字列がaとbで両方のラインを実行しましたが、彼らはボンネットの下に異なっている場合、私は

を知らないとカリング自体)は、文字列パラメータaを受け取り、文字列パラメータbを受け取って文字列a + "" + bを返す別の関数を返す関数であるということですか?

私が正しくなっている場合、addCurrは関数addの文法的砂糖だけであり、どちらもカレット関数ですか?

上記の例によれば、次の関数もスカラーと同じですか?

def add(a: String)(b: String)(c: String):String = { a + " " + b + " " + c} 

def add1(a: String)(b: String): String => String = {c => a + " " + b + " " + c} 

def add2(a:String): (String => (String => String)) = {b => (c => a + " " + b + " " + c)} 

答えて

0

カレー化されたメソッドは構文的な砂糖ですが、この部分については正しいと思います。しかし、この構文的な砂糖は少し異なります。以下の例で考えてみましょう:カリー化方法を話す

def addCur(a: String)(b: String): String = { a + b } 

def add(a: String): String => String = { b => a + b } 

val functionFirst: String => String = add("34") 
val functionFirst2 = add("34")_ 
val functionSecond: String => String = add("34") 

Generalyを部分的に適用することが可能と動作するようにScalaの暗黙のメカニズムのために必要です。上記の例では、使用例を示しましたが、2番目の例ではコンパイラが "トリック"を行うために下線記号を使用する必要があります。存在しない場合、次のようなエラーが表示されます。

Error:(75, 19) missing argument list for method curried in object XXX Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing curried_ or curried(_)(_) instead of curried .

+0

コードに間違いがありますか? functionFirst2を定義しましたが、何もしません。 functionFirstとfunctionSecondは同じです。他に何か意味がありましたか? – costa

+0

はい、addCurは文字列を返すので、 'val x = addCur(" apple ")'は実行できません。なぜなら、コンパイルは部分的なアプリケーションを作るために別の関数を返すことを期待しています。これは有効な 'val x = addCur(" apple ")_'です。 addの場合、これは有効な 'val x = add(" apple ")'関数を返すので有効です。そのとおりです?? –

+0

はいいいえ –

0

あなたの質問は私に興味がありましたので、私は自分自身を試しました。彼らは実際にはいくつかの非常に異なる構造にdesugarダウン。

def addCurr(a: String)(b: String): String = {a + " " + b}

を使用すると、これは実際にそう、それは完全に通常のアリティ-2方式作り、任意のカリー化効果を取り除く

def addCurr(a: String, b: String): String = {a + " " + b}

にコンパイルされます。 Eta展開は、カレーにするために使用されます。

def add(a:String): String => String = {b => a + " " + b}

この1つは機能1を返すメソッドをコンパイルし、あなたが期待するように動作[文字列、文字列]

+0

だから彼らは同じではありませんか?つまり、どちらも同じことですが、異なる方法でコンパイルすると、ある面では他の面より優れている必要があります。どう思いますか? –

+0

私はまだテストをしていませんが、 'def curr(a:String)(b:String):String'という形式が普通は良いと思われます(大文字小文字に依存するかもしれませんが)。直感的には、すべての引数を指定して関数をカリー化しないため、追加のオブジェクトの作成や間接指定をしない通常のメソッド呼び出しだけでなく、言語設計者が別の形式にコンパイルしないため投稿されました。 – puhlen

+0

ありがとう@Puhlen。 'def curr(a:String)(b:String)という表現が好きです。 ' –

1

彼らは少し異なった意味を持っていますが、そのユースケースはほとんど同じです実際にはどのようにしてコード内をどのように見ているのでしょうか。その数学的な意味でのScalaの関数をカリー化

をカリー化

は非常に簡単です:

val function = (x: Int, y: Int, z: Int) => 0 
// function: (Int, Int, Int) => Int = <function3> 
function.curried 
// res0: Int => (Int => (Int => Int)) = <function1> 

機能&方法

あなたは(そのScalaで事実によって混同しているように見えます=>機能は(defの方法と同じではありません。メソッドはファーストクラスのオブジェクトではありませんが、ファンクションはありません(つまり、curriedtupledのメソッドを持ち、Function1はさらに優れています)。

しかし、方法は、η拡張として知られている操作によって機能に持ち上げることができます。詳細については、this SO answerを参照してください。 methodName _と書くことで手動でトリガーすることもできますし、関数の型が予期される場所にメソッドを渡すと、暗黙的に行われます。

def sumAndAdd4(i: Int, j: Int) = i + j + 4 
// sumAndAdd4.curried // <- won't compile 

val asFunction = sumAndAdd4 _ // trigger eta expansion 
// asFunction: (Int, Int) => Int = <function2> 
val asFunction2: (Int, Int) => Int = sumAndAdd4 
// asFunction2: (Int, Int) => Int = <function2> 
val asFunction3 = sumAndAdd4: (Int, Int) => Int 
// asFunction3: (Int, Int) => Int = <function2> 


asFunction.curried 
// res0: Int => (Int => Int) = <function1> 
asFunction2.curried 
// res1: Int => (Int => Int) = <function1> 
asFunction3.curried 
// res2: Int => (Int => Int) = <function1> 
{sumAndAdd4 _}.tupled // you can do it inline too 
// res3: Int => (Int => Int) = <function1> 

複数のパラメータリストのイータ展開は

ご想像のように、ETAの拡大は

def singleArgumentList(x: Int, y: Int) = x + y 
def twoArgumentLists(x: Int)(y: Int) = x + y 

singleArgumentList _ // (Int, Int) => Int 
twoArgumentLists _ // Int => (Int => Int) - curried! 

val testSubject = List(1, 2, 3) 

testSubject.reduce(singleArgumentList) // Int (6) 
testSubject.map(twoArgumentLists) // List[Int => Int] 

// testSubject.map(singleArgumentList) // does not compile, map needs Int => A 
// testSubject.reduce(twoArgumentLists) // does not compile, reduce needs (Int, Int) => Int 

独自の関数にすべてのパラメータのリストを持ち上げるしかし、それは数学的な意味でそのカリー化ではありません。

def hmm(i: Int, j: Int)(s: String, t: String) = s"$i, $j; $s - $t" 

{hmm _} // (Int, Int) => (String, String) => String 

ここでは、2つの引数の関数を取得し、 2つの引数の関数

そして、それだけでそのargumeの一部を指定することは簡単ではありません機能と同様に、あなたが任意の大騒ぎせずバック機能を得る

val function = hmm(5, 6) _ // <- still need that underscore! 

val alreadyFunction = (i: Int, j: Int) => (k: Int) => i + j + k 

val f = alreadyFunction(4, 5) // Int => Int 

やり方あなたはそれが好きです - Scalaは多くのことについてかなり非公式です。私は、関数を部分的に適用し、それをどこかに渡す必要があるので、残りのパラメータが与えられるので、私は明示的にη拡張を行う必要はないので、複数のパラメータリストを個人的に好む私はメソッド定義サイトでより賢い構文を楽しむことができます。

+0

あなたのlasの例では、関数を返すメソッドは? –

+0

@Darth_Tatoこれはメソッドではなく、別の( '=>')関数を返す関数( '=>')です。 –

+0

ありがとう@OlegPyzhco –

関連する問題