0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Scala で LeetCode を解いてみた感想

Last updated at Posted at 2024-10-31

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 は要素が整列されている前提で、バイナリサーチを使って要素を探してくれる。

image.png

しかし、これには柔軟性があまりない。
例えば、一見 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
}

image.png

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
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?