ナベアツのプログラムをscalaでクリフトした

[ナベアツプログラム](https://e-yota.com/webservice/post-530/)を書けと言われ，その存在を初めて知った．
scalaを学ぶ良い機会かと思い，scalaで作った．

スカラといえばクリフトなので，どうせだからクリフト版にしている．

#要件と制約
- 1から「任意の数」まで順番に数える
- 3の倍数のターンはスカラを唱える
- それ以外で3がつく数字のターンはザラキを唱える

【3/4追記=====】
↑3が2回以上つくターン（331とか）では1ターンに2回も3回もザラキを唱えていたことが判明．編集リクエストを頂き，ソースコードは修正
【==========】

プログラム制約はシンプルに「for文以外はvariable禁止」．

for文以外で「ふつう変数だろ！」な部分も再帰的処理でゴリ押し．

#ソース

```scala:nabeatsu.scala
import math.pow

object nabeatsu {
  def main(args: Array[String]): Unit = {
    val num: Int = 40 //この数字まで数える
    krift(num)
  }

  //クリフトする
  def krift(num: Int): Unit = {
    for (i <- 1 to num) {
      //まず3の倍数か調べる
      if (i % 3 == 0) println(i + ": スカラ")
      //次に，1の桁が3かどうか調べる
      else if (i % 10 == 3) println(i + ": ザラキ")
      //2桁以上なら，その桁も見る
      else if (i >= 10) {
        //今見ている数値の，10の桁～最大桁それぞれが3かどうか調べる
        import scala.util.control.Breaks.{breakable, break}
        breakable {
          for (j <- 1 to calcDigit(i) - 1) {
            //具体的には10^jで割ることで桁をずらす
            //小数点はintにして丸める
            if (((i / pow(10, j)) % 10).toInt == 3) {
              println(i + ": ザラキ")
              break
            }
          }
        }
      }
    }
  }

  //数値の桁数を返す再帰的ループ
  //具体的には( 10で割った回数 + 1 ) = 桁数
  def calcDigit(fig: Int): Int = {
    if (fig < 10) 1 //例えば元々10未満なら1桁
    else calcDigit(fig / 10) + 1 //10以上なら10で割って再帰
  }
}
```

40ターンで試した結果

```console
3: スカラ
6: スカラ
9: スカラ
12: スカラ
13: ザラキ
15: スカラ
18: スカラ
21: スカラ
23: ザラキ
24: スカラ
27: スカラ
30: スカラ
31: ザラキ
32: ザラキ
33: スカラ
34: ザラキ
35: ザラキ
36: スカラ
37: ザラキ
38: ザラキ
39: スカラ
Process finished with exit code 0
```

たぶん成功．

#ためしに分析
クリフトがスカラとザラキを何回唱えたのか分析してみる．
分析は「おまけ」のため，断腸の思いでvariableを使用．

```scala:nabeatsu.scala
import math.pow

object nabeatsu {
  def main(args: Array[String]): Unit = {
    val num: Int = 10000 //この数字まで数える
    krift_count(num)
  }

  //クリフトした後，スカラとザラキの回数を返す
  def krift_count(num: Int): Unit = {
    //counts(スカラの回数, ザラキの回数, ザラキがスカラを超えたターン)
    var counts = Array(0, 0, 0)
    var flag = true //ザラキがスカラを超えたらfalseにする

    for (i <- 1 to num) {
      //まず3の倍数か調べる
      if (i % 3 == 0) {
        println(i + ": スカラ")
        counts(0) += 1
      }
      //次に，1の桁が3かどうか調べる
      else if (i % 10 == 3) {
        println(i + ": ザラキ")
        counts(1) += 1
      }
      //2桁以上なら，その桁も見る
      else if (i >= 10) {
        //今見ている数値の，10の桁～最大桁それぞれが3かどうか調べる
        import scala.util.control.Breaks.{breakable, break}
        breakable {
          for (j <- 1 to calcDigit(i) - 1) {
            //具体的には10^jで割ることで桁をずらす
            //小数点はintにして丸める
            if (((i / pow(10, j)) % 10).toInt == 3) {
              println(i + ": ザラキ")
              counts(1) += 1
              break
            }
          }
        }
      }
      //ザラキがスカラを超えたターンを1回だけ検出
      if (flag == true && counts(1) > counts(0)) {
        counts(2) = i
        flag = false
      }
    }
    println("クリフトは" + counts(0) + "回スカラを唱えた")
    println("クリフトは" + counts(1) + "回ザラキを唱えた")
    if (counts(2) > 0) println(counts(2) + "ターン目でザラキの回数がスカラを超えた")
  }

  //数値の桁数を返す再帰的ループ
  //具体的には( 10で割った回数 + 1 ) = 桁数
  def calcDigit(fig: Int): Int = {
    if (fig < 10) 1 //例えば元々10未満なら1桁
    else calcDigit(fig / 10) + 1 //10以上なら10で割って再帰
  }
}
```

5万ターンで試してみた．
結果を最後の方だけ出す．

```console
49983: スカラ
49986: スカラ
49989: スカラ
49992: スカラ
49993: ザラキ
49995: スカラ
49998: スカラ
クリフトは16666回スカラを唱えた
クリフトは15837回ザラキを唱えた
39365ターン目でザラキの回数がスカラを超えた
```

ザラキがスカラを上回る最初のターンは39365らしい．

~~単純なザラキ確率が約40%なので，まあまあのクリフトと言える．~~
何か唱えたターンのうちザラキ確率は48%ほどで，若干クリフト感が増した．

ただし5万ターンの中では30%程度なので，クリフトとしてはまだまだ．

基本的にはスカラを唱えまくるので，この後もザラキは超えつ超されつ．

#【3/4追記】ザラキとスカラは本当に超えつ超されつ？
>基本的にはスカラを唱えまくるので，この後もザラキは超えつ超されつ．

編集リクエストを受けて，ザラキとスカラは本当に超えつ超されつの関係なのか自分でも怪しくなったので検証してみた．

ザラキがスカラを超えたターンも記録していくようソース変更．

```scala:nabeatsu.scala
import math.pow

object nabeatsu {
  def main(args: Array[String]): Unit = {
    val num: Int = 43739 //この数字まで数える
    krift_count(num)
  }

  //クリフトした後，スカラとザラキの回数を返す
  def krift_count(num: Int): Unit = {
    var counts = Array(0, 0) //(0):スカラの回数, (1):ザラキの回数
    var over = Array[Int]() //ザラキがスカラを超えたターン」を追加していく
    var flag = true //ザラキがスカラを超えたらfalseにする

    for (i <- 1 to num) {
      //まず3の倍数か調べる
      if (i % 3 == 0) {
        println(i + ": スカラ")
        counts(0) += 1
      }
      //次に，1の桁が3かどうか調べる
      else if (i % 10 == 3) {
        println(i + ": ザラキ")
        counts(1) += 1
      }
      //2桁以上なら，その桁も見る
      else if (i >= 10) {
        //今見ている数値の，10の桁～最大桁それぞれが3かどうか調べる
        import scala.util.control.Breaks.{breakable, break}
        breakable {
          for (j <- 1 to calcDigit(i) - 1) {
            //具体的には10^jで割ることで桁をずらす
            //小数点はintにして丸める
            if (((i / pow(10, j)) % 10).toInt == 3) {
              println(i + ": ザラキ")
              counts(1) += 1
              break
            }
          }
        }
      }
      //ザラキがスカラを超えたターンだけ記録
      if (flag == true && counts(1) > counts(0)) {
        over = over :+ i
        flag = false
      }
      if (flag == false && counts(1) < counts(0)) flag = true
    }
    println("クリフトは" + counts(0) + "回スカラを唱えた")
    println("クリフトは" + counts(1) + "回ザラキを唱えた")
    //ザラキがスカラを超えたターンがあれば表示
    if (over.nonEmpty) {
      println("=====ザラキがスカラを超えたターン一覧=====")
      for (p <- 0 to over.size - 1) {
        println(p + 1 + "回目：" + over(p))
      }
    }
  }

  //数値の桁数を返す再帰的ループ
  //具体的には( 10で割った回数 + 1 ) = 桁数
  def calcDigit(fig: Int): Int = {
    if (fig < 10) 1 //例えば元々10未満なら1桁
    else calcDigit(fig / 10) + 1 //10以上なら10で割って再帰
  }
}
```

結果

```console
43731: スカラ
43732: ザラキ
43733: ザラキ
43734: スカラ
43735: ザラキ
43736: ザラキ
43737: スカラ
43738: ザラキ
43739: ザラキ
クリフトは14579回スカラを唱えた
クリフトは14580回ザラキを唱えた
=====ザラキがスカラを超えたターン一覧=====
1回目：39365
2回目：41309
3回目：43739
```

確かに超えつ超されつだった．

でもちょこっとしか超えてない．

#もっと簡単にクリフトする
再帰的演算なんかしなくても良い方法があった．

3が含まれるかどうかの判定は1字だけ見ればいいのだから，
計算ではなく文字で照合してしまっても良かったわけだ．

```scala:nabeatsu.scala
object nabeatsu {
  def main(args: Array[String]): Unit = {
    val num: Int = 10000 //この数字まで数える
    krift_easy(num)
  }

  //もっと簡単にクリフトする
  def krift_easy(num: Int): Unit = {
    for (i <- 1 to num) {
      //3の倍数か調べる
      if (i % 3 == 0) println(i + ": スカラ")
      //3が含まれるか調べる
      else if (checkMatch(i, '3')) println(i + ": ザラキ")
    }
  }

  //numの中にsearchが含まれているか調べる
  //含まれていればtrue, なければfalseを返す
  def checkMatch(num: Int, search: Char): Boolean = {
    if ((num.toString indexOf search) == -1) return false
    else return true
  }
}
```

#まとめ
- ナベアツプログラムをクリフトで再現
- それなりのクリフトができた
- for以外のvarを禁止したおかげか，再帰的処理を学んだ
- 関数型としてのscalaは・・・何が嬉しいか，全然わからん！
- 「スカラといえばなら魔法使い1人旅だろ」「勇者の親だろ」←同意します．ただし漁師の息子だけは道具でスカラできるしそもそも道具だけで何とかなるのでNG