【学習kotlinシリーズ】高階関数
高階関数とは
高階関数とは、関数を引数として受け取ったり、返り値として関数を返したりする関数のこと。同じような機能を持った関数を複数書いてしまう場合に、共通する部分は抽象的に一つの関数に集約してしまうことで、コードが短く書けたりするメリットが有る。
だめな例
だめな例として、文字列中の中から小文字が最初に出てくる位置を教えてくれる関数と、文字列中から最初に大文字が出てくる関数を実装することになったとして、次のように書いた。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// 文字列中の最初に小文字が出てくる位置を検索する関数 fun findLower(str: String): Int{ tailrec fun go(str: String, index: Int): Int = when{ // strが空っぽの場合、-1を返す str.isEmpty() -> -1 // strの最初の文字が小文字の場合、indexを返す str.first().isLowerCase() -> index // それ以外は再帰処理 else -> go(str.drop(1), index+1) } return go(str, 0) } // 文字列中の最初に大文字が出てくる位置を検索する関数 fun findUpper(str: String): Int{ tailrec fun go(str: String, index: Int): Int = when{ // strが空っぽの場合、-1を返す str.isEmpty() -> -1 // strの最初の文字が小文字の場合、indexを返す str.first().isUpperCase() -> index // それ以外は再帰処理 else -> go(str.drop(1), index+1) } return go(str, 0) } fun main(args: Array<String>){ val s: String = "03jr39f49Df9fmas9asef" println(findLower(s)) println(findUpper(s)) } |
実行結果
|
1 2 |
2 9 |
実装上は正しい。ただ、findLower関数とfindUpper関数をよく見てみると
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// 文字列中の最初に小文字が出てくる位置を検索する関数 fun findLower(str: String): Int{ tailrec fun go(str: String, index: Int): Int = when{ // strが空っぽの場合、-1を返す str.isEmpty() -> -1 // strの最初の文字が小文字の場合、indexを返す str.first().isLowerCase() -> index // それ以外は再帰処理 else -> go(str.drop(1), index+1) } return go(str, 0) } // 文字列中の最初に大文字が出てくる位置を検索する関数 fun findUpper(str: String): Int{ tailrec fun go(str: String, index: Int): Int = when{ // strが空っぽの場合、-1を返す str.isEmpty() -> -1 // strの最初の文字が小文字の場合、indexを返す str.first().isUpperCase() -> index // それ以外は再帰処理 else -> go(str.drop(1), index+1) } return go(str, 0) } |
when文の中の、大文字を見つける部分と小文字を見つける部分以外は全く同じコードだ。つまりその「それ以外」の部分を抽象化することで、コードを集約できる。集約したコードが以下の通り。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// 高階関数 fun find(str: String, predicate: (Char) -> Boolean): Int { tailrec fun go(str: String, index: Int): Int = when{ // strが空っぽの場合、-1を返す str.isEmpty() -> -1 // predicateの判定に引っかかった場合、indexを返す predicate(str.first()) -> index // それ以外の場合は再帰処理 else -> go(str.drop(1), index+1) } return go(str, 0) } // 文字列中の最初に小文字が出てくる位置を検索する関数 fun findLower(str: String): Int{ // findに渡す関数を定義する fun lower(c: Char): Boolean = c.isLowerCase() return find(str, ::lower) } // 文字列中の最初に大文字が出てくる位置を検索する関数 fun findUpper(str: String): Int{ // findに渡す関数を定義する fun upper(c: Char):Boolean = c.isUpperCase() return find(str, ::upper) } fun main(args: Array<String>){ val s: String = "03jr39f49Df9fmas9asef" println(findLower(s)) println(findUpper(s)) } |
実行結果
|
1 2 |
2 9 |
find関数がfindLower関数とfindUpper関数を抽象化することで、コードの集約に成功している。