Scala 簡単な抽選システムを作る

前回Pythonで作った抽選システムと同様のことをScalaで書きました。

mi12cp.hatenablog.com

pythonでプログラムを書くと、イミュータブルな書き方が出来なかったり、break文を利用してしまったりしてしまいます。 これらを排除してscalaで書き直すのが目的です。 結果、前回の抽選処理を再起を用いて記述することが出来たのですが、これが綺麗なプログラムかと言われると、少し心残りがあります。

proc()は引数が多くて少し見辛いですね。 ただ、プログラム内でshuffledが再起の度にpassingfailureに分配されていく様子はかわいいなと思いました。

csvファイルの例(applications.csv)

Scalaでのcsvの操作に慣れていないので、空白行や列が全くないファイルを前提とします。 列名も記載していません。

1,1
2,1
3,1
(省略)
198,3
199,3
200,4

build.sbt

libraryDependencies += "com.opencsv" % "opencsv" % "3.7"

プログラム(applications.scala)

import com.opencsv.CSVReader
import java.io.FileReader
import scala.util.Random.shuffle

object Main extends App {
  class Application(val id: Int, val num: Int) {
    override def toString = "id: %d, num: %d".format(id, num)
  }

  val reader = new CSVReader(new FileReader("applications.csv"))

  val applications = Iterator.continually(reader.readNext).takeWhile(_ != null)
    .map(r => new Application(r(0).toInt, r(1).toInt)).toList

  println("全応募者")
  applications.foreach(x => println("id: %d, num: %d".format(x.id, x.num)))
  println("sum: %d".format(applications.map(_.num).sum))
  println()

  def drawLots(applications: List[Application], total: Int): (List[Application], List[Application], Int) = {
    def proc(
      shuffled: List[Application], passing: List[Application], failure: List[Application], remaining: Int
    ): (List[Application], List[Application], List[Application], Int) = {
      if (shuffled.length == 0)
        (Nil, passing, failure, remaining)
      else
        if (shuffled.head.num <= remaining)
          proc(shuffled.tail, shuffled.head :: passing, failure, remaining - shuffled.head.num)
        else
          proc(shuffled.tail, passing, shuffled.head :: failure, remaining)
    }

    val shuffledApplications = shuffle(applications)
    val (_, passingApplications, failureApplications, remaining) = proc(shuffledApplications, Nil, Nil, total)
    (passingApplications.sortBy(_.id), failureApplications.sortBy(_.id), remaining)
  }

  val (passingApplications, failureApplications, remaining) = drawLots(applications, 100)

  println("当選者")
  passingApplications.foreach(x => println("id: %d, num: %d".format(x.id, x.num)))
  println("sum: %d".format(passingApplications.map(_.num).sum))
  println()

  println("落選者")
  failureApplications.foreach(x => println("id: %d, num: %d".format(x.id, x.num)))
  println("sum: %d".format(failureApplications.map(_.num).sum))
  println()

  println("残席: %d".format(remaining))
}

実行方法

  • 上記3ファイルを同ディレクトリに置く
  • sbt runで外部ライブラリの読み込みから実行までを自動で行ってくれる

実行結果

前回の記事と同様

備考