S-99: Ninety-Nine Scala Problems に挑戦中

『なっとく!関数型プログラミング』を読んでから、Scalaというプログラミング言語に興味が湧きました。しかし時間が経って結構忘れてしまっていることに気がついたので、Scalaのコードを普段から書かないといけないと思ったので、簡単な問題をScalaで解くことにしました。

S-99: Ninety-Nine Scala Problems

ちょうどいい Scala の問題集を見付けました。(元々は、L-99: Ninety-Nine Lisp Problems というLisp/Scheme用の問題集があり、その Scala版です)

 

 

例えば、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 :: _ )という書き方を始めて見たのですが、nlsの2つの変数に対してmatchを行なっているようです。Scalaのmatchは強力ですね。強力なmatchと再帰によって、宣言的に関数を定義できているのがわかります。

 

こんな感じで、自分で問題を解いた後に模範解答を見ながらScalaを学んでいきます。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


reCaptcha の認証期間が終了しました。ページを再読み込みしてください。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください