daybreaksnow's diary

私を私と呼びたい

[Scala]入門 カリー化他

以下の連載の10回目読んだ。9回目のスレッドは浅く見てもよく分からなかったのであとで。
http://www.atmarkit.co.jp/ait/kw/scala.html

名前付き引数

その名の通り、引数指定時に変数名を表記できる。引数の順番も変えられる。
かゆいところに手が届く機能だと感じる。

def div(x:Int,y:Int) = x/y
val d = div(y = 5,x = 10)

エンティティのコピー

case classにするとディープコピーを行うcopyメソッドを定義してくれる
copyの引数にはコンストラクタ引数を指定できる。何も指定しなければコピー元の値がそのまま使われる
ここで名前付き引数を使うと便利?

case class X(name:String){
  var y:Y = new Y(name + "Y")
}
class Y(var name:String)

val x = X("hoge")
val x2 = x.copy()
x.y.name = "fuga"
println(x.y.name)
println(x2.y.name) //ディープコピーされているのでhogeY

引数リスト

(a:Int,b:Int)をdef (a:Int)(b:Int)と書ける。
呼び出しもhoge(1)(2)と別々の括弧になる。
関数を渡す時は引数リストを使うとみやすくなる?
使いどころは後述するカリー化の時?

//関数定義
 def myLoop(c:Int, func : => Unit) = {
  for (i <- 1 to c){
   func
  }
 }
//関数呼び出し
 myLoop(3){println("hoge")}


引数有の関数を受け取る場合の例。あまり見栄えがよくないか。

//関数定義
def functest(c:Int)(func : (Int) =>Unit ) = {
 func(c)
}
//関数呼び出し
functest(1){
  (c:Int) => println(c+1)
}

引数リストの部分適用

引数リスト1つの場合と同じように部分適用できる。

def sum(a:Int)(b:Int) = a+b

val partSum = sum(_:Int)(2)
val fSum = sum _
val partSum2 = fSum(_:Int)(2)

カリー化

複数の引数を取る関数を、一つの引数を取る関数のチェインに分割すること

カリー化前

def sum(a:Int,b:Int,c:Int) = a + b + c

カリー化後(冗長に書いたもの)

def sumCurrey(a:Int) : (Int) => ((Int)=>Int) = {
  def innerB(b:Int) : (Int)=>Int = {
    def innerC(c:Int) : Int = {
      return a + b + c
    }
    return innerC _
  }
  return innerB _ 
}

型指定を省略

def sumCurrey(a:Int)  = {
  def innerB(b:Int)  = {
    def innerC(c:Int)  = {
      a + b + c
    }
    innerC _
  }
  innerB _
}

innerCを無名関数に

def sumCurrey(a:Int)  = {
  def innerB(b:Int)  = {
    (c:Int) => a + b + c
  }
  innerB _
}

innerBを無名関数に

def sumCurrey(a:Int)  = {
  (b:Int) => (c:Int) => a + b + c
}

最終的にここまでシンプルになる

def sumCurrey(a:Int) = (b:Int) => (c:Int) => a + b + c

カリー化のうれしいこと

後でまとめる。参考
http://qiita.com/KDKTN/items/6a27c0e8efa66b1f7799

なお、カリーとは数理論理学者のHaskell Curryに由来する。