LL脳がscalaの勉強を始めたよ その22
Scalaコップ本の8章の続きをやりますよー、プレースホルダー構文ですよ
プレースホルダー構文
関数リテラルを簡潔にする方法の1つとしてプレースホルダーがあるみたいデス
引数を取る場所を用意するんだけど、具体的な値は指定したくないときに" _ "で指定する方法がプレースホルダー…って解釈でいいのかしら?
とりあえずサンプル、サンプル
// Listを定義しますよー scala> val l = List(1,2,3,4) l: List[Int] = List(1, 2, 3, 4) // プレースホルダー使ってみますよ scala> l.filter(_ > 2) res0: List[Int] = List(3, 4) // 意味的にはこれと同じっぽいですけど、確かに簡潔だ scala> l.filter(x => x>2) res2: List[Int] = List(3, 4)
いまいち実感がわかないので内容について写経すると
パラメーターが関数リテラル内で一度しか使われない場合には、1個以上のパラメーターのプレースホルダーとしてアンダースコアを使うことができる
??(´・ω・`)??
コンパイラがアンダースコアがある場所を見つけると自動的に引数の当てはまる場所だと解釈して、引数を自動的に当てはめていきますよー、ということで良いのかしら?(´ε`;)ウーン…
コンパイラが推論できない場合
例えばこんな感じの記述だとコンパイラ様が推論できないそうです
// こんな感じの記述だと推論できないそうです scala> val f = _ + _ // エラーメッセージはこんな感じ、何の型かわからんよ!!とのこと <console>:4: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2)) val f = _ + _ ^ <console>:4: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2)) val f = _ + _ ^
型が推論できないそうなので、型を指定してあげましょうかねー
// 型を指定してみます scala> val f = (_:Int) + (_:Int) f: (Int, Int) => Int = <function> // これだと実行できますねー scala> f(3,4) res3: Int = 7
アンダースコアの数で引数の個数になるよ、なるよ!
最初の写経で”パラメーターが関数リテラル内で一度しか使われない場合”とある通り、" _ "でパラメータを置き換えられるのは1回だけで、” _ ”が複数ある場合はその分引数がある(`・ω・´)と解釈されるよう。
具体的に言うと上のサンプルだったら下のような引数2個とる関数になるよ!てことみたい
scala> val f = (a:Int,b:Int) => a + b f: (Int, Int) => Int = <function> scala> f(3,4) res4: Int = 7
だから単純にパラメータ置き換え!と考えて、こういうアホな置換えをすればまったく別の定義になってしまうよ、ってことですな
// 1個の引数を2回使う関数です scala> val f1 = (x:Int) => x * x f1: (Int) => Int = <function> // 実行しますよ scala> f1(2) res5: Int = 4 // プレースホルダーで(それっぽく)置き換えますよ scala> val f1 = (_:int) * (_:Int) warning: there were deprecation warnings; re-run with -deprecation for details f1: (int, Int) => Int = <function> // 引数二つなんだから実行できねよーヽ(`Д´)ノウワァァァン!! scala> f1(2) <console>:6: error: wrong number of arguments for method apply: (int,Int)Int in trait Function2 f1(2) ^
うん、なるほど。なんとなくわかったような気がする。名前そのまんまだけどプレースホルダーは(一回限りの)使い捨ての格納場所になるわけね。