あるプログラマの日記

プログラマのメモ、出来事、考えたこと、勉強とかの雑記

ローカル関数、プレースホルダー

ローカル関数

関数定義のスコープ内に、関数を定義できる。
同じスコープ内からしか見えないローカル関数になる。
ローカル関数は外側の関数の引数や変数にアクセスできる。

scala> def foo(list: List[String], n:Int) = {
     |   def bar(s: String) = if (s contains n.toString) n else -1
     |   list.map { str => bar(str) }
     | }
foo: (list: List[String], n: Int)List[Int]

scala> foo(List("100","35","98","73"), 5)
res1: List[Int] = List(-1, 5, -1, -1)

scala>  foo(List("100","35","98","73"), 3)
res2: List[Int] = List(-1, 3, -1, 3)

関数 foo 内部に関数 bar を定義。関数 bar は foo の引数 n を自由変数
として取り込んだクロージャ

ローカル関数とは関係ないが、List の map は引数が 1つの Function1 型を
引数としてとり、リストの各要素に順次適用する。

プレースホルダー構文

関数内で使用する引数が引数の数以下で順序通りの使用で
関数への引数が関数のコードブロック中に1度しか使われない場合は
アンダースコア "_" を使用して、引数指定個所を _ に置き換えることができる。

scala> List(1,2,3,4,5).map { n => n.toString }
res3: List[java.lang.String] = List(1, 2, 3, 4, 5)

scala>  List(1,2,3,4,5).map { _.toString }
res4: List[java.lang.String] = List(1, 2, 3, 4, 5)

引数が一つで戻り値が Unit の関数を引数に指定する場合は
暗黙にメソッドの引数を Scala が補完してくれるので
完全に省略できる。

scala> List(1,2,3).foreach { n => println(n) }
1
2
3

scala> List(1,2,3).foreach { println(_) }
1
2
3

scala> List(1,2,3).foreach { println }
1
2
3


引数が2つある場合も、関数ブロック内で利用する引数が指定引数の数以下で
同じ順序であれば使用できる。

scala> val hoge: (Int, Int) => Int = { _ * _ }
hoge: (Int, Int) => Int = <function2>

プレースホルダーを使用した関数リテラルの引数と戻り値の型が推論できない場合は、代入先の
変数に関数の型を指定する必要がある。

上の map や foreach の後が {..} になっているのは
関数の引数が1つの場合は、丸括弧 (..) を省略してかわりに {..} で囲んで書くことができる。

scala> def foobar(i: Int) = i * 2
foobar: (i: Int)Int

scala> foobar(2)
res7: Int = 4

scala> foobar({4})
res8: Int = 8

scala> foobar{6}
res9: Int = 12