算譜王におれはなる!!!!

偏りはあると思うけど情報技術全般についてマイペースに書くよ。

落ち着け...素数を数えて落ち着くんだ... #kotlin

FizzBuzzブームが再び巻き起こり、鎮火したかと思ったら「整数を数えていき、素数のときにJOJOと表示する」遊びがひそかに流行している。

解いてみた

Kotlinで解いてみた。

fun main(args : Array<String>) {
  for(i in 1..100) {
    i.jojo()
  }
}

private fun Int.jojo() {
  val s = if(this.isPrime()) "JOJO!" else "$this"
  println(s)
}

private fun Int.isPrime() : Boolean {
  check(this > 0)
  
  if(this == 1) return false
  if(this == 2) return true
  if(this % 2 == 0) return false
  
  for(i in Array((this - 3) / 4) {
    it * 2 + 3
  }) {
    if(this % i == 0 && this != i) {
      return false
    }
  }
  
  return true
}

解説

main()とjojo()はいいとして、注目してもらいたいのがisPrime()関数。
これはIntの拡張関数ということで定義した。

関数の中で最初に登場するcheck()関数。
これはオブジェクトの状態チェックを行う関数で、引数の値がfalseの場合、IllegalStateExceptionを投げる。
似たような関数にassert(), require()などがKotlinの標準ライブラリから提供されているよん。

次の3つ続くif式は、無駄な計算を省くために置いた。
1は素数じゃないし、2は素数。2の倍数は素数じゃないですよ。
って書いておくことで、後のシラミ潰しを少しでも楽にしようとしている。

forループ内で、そのシラミ潰し作業。
要は、自分自身(関数のレシーバのInt)を割り切れる数が存在するかどうかを調べる。
forのカッコ内では配列を作り、その要素に1つずつアクセスするループにしているんだけど、
3から始まり、公差が2の配列を作っている。
Arrayのコンストラクタは、配列の要素数と、初期化のための関数を取るからそれができる。

あとは単純に素数判定。

おまけ

@_siosio_さんもこの問題をKotlinで解かれているので、勝手に解説しちゃいますw
素数の時にJOJOと出力するKotlin — Gist

jojoIterator()は、実際に画面に出力する値のイテレータを返す関数。
関数内で真っ先に目に入るのが、ローカル関数のnext()。
これは1つの数値に対して素数判定を行い、画面に出力する値を返す。

そして、素数判定の仕方がKool!
1と2に関しては、僕のと同様ハードコード。
それ以外の数である場合には、次のように判定している。
「2から、その数の平方根(端数切り捨て)までの連続する整数列に対して、その数を割り切れる要素が1つもなければ素数である」

これをKotlinで次のように表現している。

「2から、その数の平方根(端数切り捨て)までの連続する整数列」を作る

val r = (2..Math.sqrt(number.toDouble()).toInt())

「その数を割り切れる要素が1つもなければ素数である」

if (r.any {number % it == 0}) {
    number
} else {
    "JOJO"
}

any()は、いずれかの要素が指定した条件を満たしたときにtrueを、それ以外の場合にfalseを返す関数。
ここで指定した条件は number % it == 0、つまり素数判定したい数が要素で割り切れるかどうか。
1つでも割り切れる要素が存在すれば、any()はtrueを返すので、素数ではない。
falseの場合は素数なので "JOJO" を返す。