Scala中的链式比较

时间:2009-07-22 11:36:50

标签: scala

Python支持“链式比较”的优雅语法,例如:

0 <= n < 256

的含义,

0 <= n and n < 256

在语法上知道它是一种相当灵活的语言,是否可以在Scala中模拟这个功能?

2 个答案:

答案 0 :(得分:8)

丹尼尔的答案是全面的,实际上很难添加任何东西。由于他的答案提出了他提到的一个选择,我只想加上我的2美分,并以另一种方式提出一个非常短的解决方案。丹尼尔的描述:

  

理论上,你可以喂养   其中一种方法的结果   另一种方法。我能想到两个   这样做的方法:

     
      
  • 具有&lt; =返回具有接收到的参数和比较结果的对象,并且具有&lt;适当使用这两个值。
  •   

CmpChain将作为已经与免费最右边对象进行比较的累加器,以便我们可以将它与下一个对象进行比较:

class CmpChain[T <% Ordered[T]](val left: Boolean, x: T) {
  def <(y: T) = new CmpChain(left && x < y, y)
  def <=(y: T) = new CmpChain(left && x <= y, y)
  // > and >= are analogous

  def asBoolean = left
}

implicit def ordToCmpChain[T <% Ordered[T]](x: T) = new AnyRef {
  def cmp = new CmpChain(true, x)
}
implicit def rToBoolean[T](cc: CmpChain[T]): Boolean = cc.asBoolean

您可以将其用于任何有序类型,例如IntDouble s:

scala> (1.cmp < 2 < 3 <= 3 < 5).asBoolean                          
res0: Boolean = true

scala> (1.0.cmp < 2).asBoolean
res1: Boolean = true

scala> (2.0.cmp < 2).asBoolean
res2: Boolean = false

隐式转换会产生Boolean,它应该是:

scala> val b: Boolean = 1.cmp < 2 < 3 < 3 <= 10  
b: Boolean = false

答案 1 :(得分:5)

不是真的。需要记住的是,除了几个关键字之外,Scala中的所有内容都是对象的方法调用。

所以我们需要调用“&lt; =”和“&lt;”对象上的方法,每个这样的方法都接收一个参数。那么,你需要四个对象,有三个显式对象,两个隐含对象 - 每个方法的结果。

理论上,您可以将其中一个方法的结果提供给另一个方法。我可以想到两种方法:

  • 让&lt; =返回一个既包含接收参数又包含比较结果的对象,并且具有&lt;适当地使用这两个值。

  • 具有&lt; =返回false或者它接收的参数,并且具有&lt;要么失败,要么与其他参数进行比较。这可以使用Either类或基于它的东西来完成。

事实上,这两种解决方案非常相似。

一个问题是这种比较运算符的消费者期望布尔结果。这实际上是最容易解决的问题,因为你可以定义从[布尔,T]到布尔值的隐式。

所以,从理论上讲,这是可能的。你可以用自己的一类来做。但是,您将如何改变已定义的现有方法?着名的Pimp My Class模式用于添加行为,而不是更改行为。

以下是第二个选项的实现:

object ChainedBooleans {

  case class MyBoolean(flag: Either[Boolean, MyInt]) {
    def &&(other: MyBoolean): Either[Boolean, MyInt] = 
      if (flag.isRight || flag.left.get) other.flag else Left(false)

    def <(other: MyInt): Either[Boolean, MyInt] = 
      if (flag.isRight || flag.left.get) flag.right.get < other else Left(false) 
    def >(other: MyInt): Either[Boolean, MyInt] = 
      if (flag.isRight || flag.left.get) flag.right.get > other else Left(false) 
    def ==(other: MyInt): Either[Boolean, MyInt] = 
      if (flag.isRight || flag.left.get) flag.right.get == other else Left(false) 
    def !=(other: MyInt): Either[Boolean, MyInt] = 
      if (flag.isRight || flag.left.get) flag.right.get != other else Left(false) 
    def <=(other: MyInt): Either[Boolean, MyInt] = 
      if (flag.isRight || flag.left.get) flag.right.get <= other else Left(false) 
    def >=(other: MyInt): Either[Boolean, MyInt] = 
      if (flag.isRight || flag.left.get) flag.right.get >= other else Left(false) 
  }

  implicit def toMyBoolean(flag: Either[Boolean, MyInt]) = new MyBoolean(flag)
  implicit def toBoolean(flag: Either[Boolean, MyInt]) = 
    flag.isRight || flag.left.get 

  case class MyInt(n: Int) {
    def <(other: MyInt): Either[Boolean, MyInt] =
      if (n < other.n) Right(other) else Left(false)

    def ==(other: MyInt): Either[Boolean, MyInt] =
      if (n == other.n) Right(other) else Left(false)

    def !=(other: MyInt): Either[Boolean, MyInt] =
      if (n != other.n) Right(other) else Left(false)

    def <=(other: MyInt): Either[Boolean, MyInt] = 
      if (this < other || this == other) Right(other) else Left(false)

    def >(other: MyInt): Either[Boolean, MyInt] =
      if (n > other.n) Right(other) else Left(false)

    def >=(other: MyInt): Either[Boolean, MyInt] = 
      if (this > other || this == other) Right(other) else Left(false)
  }

  implicit def toMyInt(n: Int) = MyInt(n)
}

这是一个使用它的会话,显示可以做什么和不能做什么:

scala> import ChainedBooleans._
import ChainedBooleans._

scala> 2 < 5 < 7
<console>:14: error: no implicit argument matching parameter type Ordering[Any] was found.
       2 < 5 < 7
         ^

scala> 2 < MyInt(5) < 7
res15: Either[Boolean,ChainedBooleans.MyInt] = Right(MyInt(7))

scala> 2 <= MyInt(5) < 7
res16: Either[Boolean,ChainedBooleans.MyInt] = Right(MyInt(7))

scala> 2 <= 5 < MyInt(7)
<console>:14: error: no implicit argument matching parameter type Ordering[ScalaObject] was found.
       2 <= 5 < MyInt(7)
         ^

scala> MyInt(2) < 5 < 7
res18: Either[Boolean,ChainedBooleans.MyInt] = Right(MyInt(7))

scala> MyInt(2) <= 5 < 7
res19: Either[Boolean,ChainedBooleans.MyInt] = Right(MyInt(7))

scala> MyInt(2) <= 1 < 7
res20: Either[Boolean,ChainedBooleans.MyInt] = Left(false)

scala> MyInt(2) <= 7 < 7
res21: Either[Boolean,ChainedBooleans.MyInt] = Left(false)

scala> if (2 <= MyInt(5) < 7) println("It works!") else println("Ow, shucks!")
It works!