6

私の同僚は、次のような質問を送ってくれました。

カリー化を実行する HOF (高次関数) を実装します。関数のシグネチャは次のとおりです。

def curry[A,B,C](f:(A,B) => C) : A => B => C

同様に、次のように非カリー化を実行する関数を実装します。

def uncurry[A,B,C](f:A => B => C): (A,B) => C

私がカリー化を理解している方法は、複数のパラメーターを取る関数がある場合、結果が得られるまで各パラメーターに関数を繰り返し適用できるということです。

したがって、何かが????にf:(A,B) => C変わります。A => f(A,_) => f(B)

そして uncurrying は、次のようにこのアプリケーションを 1 つの関数に統合することです。

f:A=>B=>Cでしょうf(A,B)か?

ここの構文に混乱しているだけかもしれませんが、ここで欠けているものを誰かが指摘できれば幸いです。

ありがとう

4

2 に答える 2

12

たくさんのコメントを含むこの完全に機能する例が理解しやすいことを願っています。質問があれば返信してください。

このコードを Scala インタープリターにドロップすることで実行できます。

// Here's a trait encapsulating the definition your coworker sent.
trait Given {
  def curry[A,B,C](f:(A,B) => C) : A => B => C
  def uncurry[A,B,C](f:A => B => C): (A,B) => C
}

object Impl extends Given {
  // I'm going to implement uncurry first because it's the easier of the
  // two to understand.  The bit in curly braces after the equal sign is a
  // function literal which takes two arguments and applies the to (i.e.
  // uses it as the arguments for) a function which returns a function.
  // It then passes the second argument to the returned function.
  // Finally it returns the value of the second function.
  def uncurry[A,B,C](f:A => B => C): (A,B) => C = { (a: A, b: B) => f(a)(b) }

  // The bit in curly braces after the equal sign is a function literal
  // which takes one argument and returns a new function.  I.e., curry()
  // returns a function which when called returns another function
  def curry[A,B,C](f:(A,B) => C) : A => B => C = { (a: A) => { (b: B) => f(a,b) } }
}

def add(a: Int, b: Long): Double = a.toDouble + b
val spicyAdd = Impl.curry(add)
println(spicyAdd(1)(2L)) // prints "3.0"
val increment = spicyAdd(1) // increment holds a function which takes a long and adds 1 to it.
println(increment(1L)) // prints "2.0"
val unspicedAdd = Impl.uncurry(spicyAdd)
println(unspicedAdd(4, 5L)) // prints "9.0"

数値の少ない例はどうですか?

def log(level: String, message: String) { 
  println("%s: %s".format(level, message)) 
} 
val spicyLog = Impl.curry(log) // spicyLog's type is String => Unit
val logDebug = spicyLog("debug") // This new function will always prefix the log
                                 // message with "debug".
val logWarn = spicyLog("warn") // This new function will always prefix the log 
                               // message with "warn".
logDebug("Hi, sc_ray!") // prints "debug: Hi, sc_ray!"
logWarn("Something is wrong.") // prints "warn: Something is wrong."

更新 「コンパイラは などの式をどのように評価しますか?」という質問に答えましたa => b => f(a,b)。そうではありません。少なくとも、同僚のスニペットで定義されている方法では、コンパイルされません。A => B => Cただし、一般的には、「A を引数として受け取る関数。これは、B を引数として取り、C を返す関数を返します」という形式のものを目にする場合です。

于 2012-12-10T02:48:42.433 に答える
8

私はあなたの質問を本当に理解しているかどうか確信が持てません.実際の実装以外に何を知りたいですか? 説明したように、それは非常に簡単なはずです。

def curry[A,B,C](f:(A,B) => C): A => B => C = 
  a => b => f(a,b)

意味a => b => f(a,b)は、「戻り値がである 1の引数の関数、また、戻り値が実行されたものである (型が である) 1 つの引数の関数」ですab => f(a,b)bf(a,b)C

a => b => f(a, b)役立つ場合は、もう少し冗長に書くことができますか?

 { (a: A) => {           // a function of *one* argument, `a`
      (b: B) => {        // a function of *one* argument, `b`
         f(a, b)         // whose return value is what you get of you execute `f(a,b)` (whose type is `C`)
      }
   }
 }

def uncurry[A,B,C](f:A => B => C): (A,B) => C = 
  (a, b) => f(a)(b)

where(a, b) => f(a)(b)は、「2 つの引数の関数であり、その戻り値は最初に HoFに(a, b)適用したときに得られるものであり、次に を消費して を返す関数を返す」という意味です。afbC

それは役に立ちますか?

于 2012-12-10T02:35:33.827 に答える