kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Python風比較演算子をScalaで実装してみた

Pythonでは0 < i < 10みたいに、数式っぽい形で数値の比較を行うことができる。これを、Scalaで(無理やり)エミュレートしてみた。汎用的に作ろうと思えば、もっと汎用的に作れるけどめんどいので、とりあえずInt型のみ対応。実装を見ればわかるけど、数式の構文木を作ってそれをevalしているようなものなので、(たぶん)かなり遅い。implicit conversionを使って、Boolean型が要求された時点で、比較演算の構文木をevalしてBoolean型を返すようにしているのがミソ。

object Op extends Enumeration {
  val LT = Value("<")
  val GT = Value(">")
  val LTE = Value("≦")
  val GTE = Value("≧")
  val AND = Value("∧")
}
import Op._

abstract class Exp 
case class BinOp(kind: Op.Value, lhs: Exp, rhs: Exp) extends Exp {
  def <(rhs: Int) = BinOp(AND, this, BinOp(LT, this.rhs, IntExp(rhs)))
  def >(rhs: Int) = BinOp(AND, this, BinOp(GT, this.rhs, IntExp(rhs)))
  def ≦(rhs: Int) = BinOp(AND, this, BinOp(LTE, this.rhs, IntExp(rhs)))
  def ≧(rhs: Int) = BinOp(AND, this, BinOp(GTE, this.rhs, IntExp(rhs)))
}
case class IntExp(value: Int) extends Exp
class RichInt(val lhs: Int) {
  def <(rhs: Int) = BinOp(LT, IntExp(lhs), IntExp(rhs))
  def >(rhs: Int) = BinOp(GT, IntExp(lhs), IntExp(rhs))
  def ≦(rhs: Int) = BinOp(LTE, IntExp(lhs), IntExp(rhs))
  def ≧(rhs: Int) = BinOp(GTE, IntExp(lhs), IntExp(rhs))
}
implicit def toRichInt(x: Int): RichInt = new RichInt(x)
implicit def BinOp2Boolean(x: BinOp): Boolean = x match {
  case BinOp(LT, lhs:IntExp, rhs:IntExp) => lhs.value < rhs.value
  case BinOp(GT, lhs:IntExp, rhs:IntExp) => lhs.value > rhs.value
  case BinOp(LTE, lhs:IntExp, rhs:IntExp) => lhs.value <= rhs.value
  case BinOp(GTE, lhs:IntExp, rhs:IntExp) => lhs.value >= rhs.value
  case BinOp(AND, lhs:BinOp, rhs:BinOp) => BinOp2Boolean(lhs) && BinOp2Boolean(rhs)
}

使い方は以下のような感じ。

val i = 9
if(0 < i < 10) {
  println("0 < i < 10")
} else {
  println("i ≦ 0 || i ≧ 10")
}

実行結果。

0 < i < 10