S-99: Ninety-Nine Scala Problems に挑戦中
![](https://blog.starbug1.com/wp-content/uploads/2025/02/scala-spiral.png)
『なっとく!関数型プログラミング』を読んでから、Scalaというプログラミング言語に興味が湧きました。しかし時間が経って結構忘れてしまっていることに気がついたので、Scalaのコードを普段から書かないといけないと思ったので、簡単な問題をScalaで解くことにしました。
S-99: Ninety-Nine Scala Problems
ちょうどいい Scala の問題集を見付けました。(元々は、L-99: Ninety-Nine Lisp Problems というLisp/Scheme用の問題集があり、その Scala版です)
https://t.co/NRxvnLbCyE これを頭からゆっくり進めてる。↓のような感じで進めればScalaについて勉強になるので良い。
自分で回答を書いてみる。 答(複数ある)を見て、理解する。(知らないメソッドとか解法が勉強になる) 知らないメソッドは、REPLで動かしてみたりする。 最適な解法を写経する。 — しめじ(smeghead) (@smeghead) February 6, 2025
例えば、P03 の問題をやってみます。
P03 (*) Find the Kth element of a list.
By convention, the first element in the list is element 0. Example:scala> nth(2, List(1, 1, 2, 3, 5, 8)) res0: Int = 2
リストからn番目の要素を取得する関数nth
を定義する問題です。
テストコードを作成しておきます。
package test.S99
import S99.WorkingWithLists._
class WorkingWithListsTest extends munit.FunSuite {
test("P03 (*) Find the Kth element of a list.") {
assertEquals(1, nth(0, List(1, 1, 2, 3, 5, 8)))
assertEquals(2, nth(2, List(1, 1, 2, 3, 5, 8)))
assertEquals(5, nth(4, List(1, 1, 2, 3, 5, 8)))
assertEquals(8, nth(5, List(1, 1, 2, 3, 5, 8)))
assertEquals("a", nth(0, List("a", "b", "c")))
assertEquals("b", nth(1, List("a", "b", "c")))
assertEquals("c", nth(2, List("a", "b", "c")))
}
}
リストの位置を指定して、要素を取得する関数のテストを書きました。
package S99
object WorkingWithLists {
// P03 (*) Find the Kth element of a list.
def nth[A](n: Int, xs: List[A]): A = xs(n)
}
関数にするまでもない処理なのでそのまま普通に書きました。
実装できたので、テストの実行をして問題ないことが確認できました。
sbt:scala-practice1> testOnly test.S99.WorkingWithListsTest
[info] compiling 1 Scala source to /usr/src/target/scala-3.5.0/test-classes ...
test.S99.WorkingWithListsTest:
+ P03 (*) Find the Kth element of a list. 0.005s
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
[success] Total time: 0 s, completed Feb 7, 2025, 12:35:14 PM
その後、Solutionリンクから回答を確認します。
// P03 (*) Find the Kth element of a list.
// By convention, the first element in the list is element 0.
//
// Example:
// scala> nth(2, List(1, 1, 2, 3, 5, 8))
// res0: Int = 2
object P03 {
// Trivial with builtins.
def nthBuiltin[A](n: Int, ls: List[A]): A =
if (n >= 0) ls(n)
else throw new NoSuchElementException
// Not that much harder without.
def nthRecursive[A](n: Int, ls: List[A]): A = (n, ls) match {
case (0, h :: _ ) => h
case (n, _ :: tail) => nthRecursive(n - 1, tail)
case (_, Nil ) => throw new NoSuchElementException
}
}
簡単な解法(nthBuiltin
)と再帰による解法(nthRecursive
)が、示されていました。自分の回答は、簡単な解法に近かったんですが、模範解答ではn
が マイナスの値なら、NoSuchElementException
をスローしていました。
IndexOutOfBoundsException
がスローされる代わりにNoSuchElementException
を投げているのはわかりますが、それならマイナスの場合だけでなく、n
が大きすぎる場合にも、対応しないと片手落ちではないですかね?
一方、再帰による解法を見てみると、case (0, h :: _ )
という書き方を始めて見たのですが、n
とls
の2つの変数に対してmatch
を行なっているようです。Scalaのmatch
は強力ですね。強力なmatch
と再帰によって、宣言的に関数を定義できているのがわかります。
こんな感じで、自分で問題を解いた後に模範解答を見ながらScalaを学んでいきます。