🦁

一日一処: Scalaのオーバーロードでキメラを作り出す

2024/02/03に公開

演算子オーバーロード

オーバーロードは、ある特定の機能を有する演算子を作り出すことを指す。Scalaにはこの機能が備わっており、本来持っている機能とは異なる処理を状況によって変えることができる。

用途

実際の用途については、多岐にわたるが、値の比較や変化など、処理として複数記述しなければならない共通のものを演算子をお借りして、実装するという具合だ。ただ、用いすぎてしまうと、初めてそのプロジェクトに参加した人が、混乱してしまうため、ある程度の個数にとどめておくと良いだろう。また、この演算子のオーバーロードは、他の言語でも見られるため、使用している言語が機能を持っているか確認して試してみるのもいいかもしれない。

オーバーロードする

まずは、人間用のクラスを作り、Bobを生み出す。一般的なクラスを用いた定義だ。

class Human(var name: String, var age: Int) {}

val bob = Human("bob", 20)
println(bob.name) // bob

次に、Bobだけだと世界に一人で寂しいため、もう一人、Aliceを生み出そう。

class Human(var name: String, var age: Int) {}

val bob = Human("bob", 20)
val alice = Human("alice", 20)
println(bob.name) // bob
println(alice.name) // alice

世界の始まりのように、ひとまず一対の人間を生み出すことに成功した。ただ、今回のテーマは、演算子のオーバーロードだ。人間の演算子にオーバーロードを与える前に、まずは、数値における、オーバーロードを試してみる。

println(1 + 1)

これは、通常の数値を加算するコードだ。答えはもちろん2になる。ただし、日本人が1+1なんて問われると、基本的には「田んぼの田」が答えになるだろう。よって、この様にコードを変更すると、答えは、となる。すばらしい。

implicit class IntPlus(private val a: Int) extends AnyVal {
  def(b: Int): Int|String = if(a == 1 && b == 1) "田" else a + b
}

println(11) // 田

これで、日本人にとっての正解にありつけた。ただ、若干ズルをしている。先程までの加算記号は半角のプラス記号だったが、こちらは、全角のプラス記号だ。半角のプラス記号に対して上書きができないため、全角のプラス記号に意味を持たせて、この様に計算結果が出力されるようにした。

最後に、話を戻して、同様に人間クラスの加算を行ってみる。

class Human(var name: String, var age: Int) {}

val bob = Human("bob", 20)
val alice = Human("alice", 20)
println(bob + alice)

このコードだと、しっかりとエラーになる。value + is not a member of Playground.Humanということで、中央のプラス記号に対して、処理が設定されていないため、怒られてしまう。そのため、人間のクラスにプラス記号を与えてみる。

class Human(var name: String, var age: Int) {
  def + (b: Human): String = {
    return "人間の加算"
  }
}

val bob = Human("bob", 20)
val alice = Human("alice", 20)
println(bob + alice) // 人間の加算

数値の加算記号と異なり、こちらは、半角の記号で問題ない。メソッド内に設定した文字がそのまま出力されるようになった。問題なく、加算記号が動作しているようだ。
それでは最後に、加算の処理を適切に記述して終わる。

import scala.util.Random

class Human(var name: String, var age: Int) {
  def + (b: Human): Human = {
    val newName = Random.shuffle((name + b.name).split("")).mkString("")
    val newAge = age + b.age
    return new Human(newName, newAge)
  }
}

val bob = Human("bob", 20)
val alice = Human("alice", 20)
val chimera = bob + alice
println(chimera.name) // eolabibc

これで、BobとAliceを融合してchimeraを作り出すことに成功した。名前は、結合した上で、ランダムに配置した。よりキメラ感が増してくるだろう。さすがにeolabibcの読み方もわからない。

Discussion