Scala を手に馴染ませるために 100問ほど解いた。
気がついたことを雑にメモる。
1. map / foreach 関数 と pattern matchin の相性が良い
コードを綺麗にかける。
例: 1047. Remove All Adjacent Duplicates In String
val stack = mutable.Stack[Char]()
s.foreach({
// pattern matching anonymous function を使って
// `c match {` が省略できた。
case char if stack.isEmpty => stack.push(char)
case char if stack.top == char => stack.pop
case char => stack.push(char)
}
})
2. 関数型で処理を書く際には、foldLeftが活躍する
例: array の各要素を mapに追加する
array.foldLeft(Map())((map, keyValPair) => {
val (key, value) = keyValPair
map.updated(key, value)
})
3. mutable.PriorityQueue
は max heap
Python の heapq
は min heap だった。
4. バイナリサーチを自前実装しなければいけないケースが多い
IndexedSeq#search
は要素が整列されている前提で、バイナリサーチを使って要素を探してくれる。
しかし、これには柔軟性があまりない。
例えば、一見 Python の bisect_left
に挙動が近いが、探している値が複数個存在する場合に、最初のインデックスが返ってくる保障はない。
細かいロジックを実現したい場合は、自前でバイナリサーチを実装する必要がある。
5. Ordering オブジェクトの自前実装
PriorityQueue, Sorting, Binary Search などで使用する要素に対して独自の順位付けをしたい時に実装する必要がある。
例えば以下。
implicit object ordering extends Ordering[(Int, Int)] {
override def compare(x: (Int, Int), y: (Int, Int)): Int =
if (x._1 != y._1) y._1 - x._1
else x._2 - y._2
}
6. 冪乗の演算子は存在しない
2の5乗は、2 ** 5
ではなく、Math.pow(2, 5)
と書く。
7. 任意の数を底とする対数を求めるのにも一苦労。
log2 N は、 loge N / loge 2 であることを利用し、 Math.log(10) / Math.log(2)
を使う。
8. 問題によっては答えを Long 型で返す必要がある
メソッドのシグニチャーで Long 型を返すように指定されているが、Int をリターンするコードを書いた場合、Int→Long の暗黙の型変換によりコンパイルは通ってしまうので注意が必要。
9. ループ内の continue が存在しない
flag や if文を使って制御しよう。
10. ループ内の break も存在しない
解決策(1). flagを使って制御する。
var finished = false
for (num <- nums if !finished) {
....
finished = true
}
解決策(2). breakable
を使う
import scala.util.control.Breaks._
breakable {
for{num <- sortedNums} {
if(max < num) break()
println(num)
}
}
10. for ループ内で early return できない
解決策の一つは、boundary
を使うこと。
import scala.util.boundary, boundary.break
def hasTarget(nums: Array[Int], target: Int): Boolean = {
boundary {
for{num <- nums} {
if(num == target) break(true)
}
false
}
}
ちなみに、early return しようとすると以下のようなエラーが発される。
non local returns
とのことなので、ラムダ関数の中から early return するのも不可能だという認識。
Line 13 Char 24: Warning (in solution.scala)
13 | if (isInvalidRow) return false
| ^^^^^^^^^^^^
|Non local returns are no longer supported; use `boundary` and `boundary.break` in `scala.util` instead