Python支持“链式比较”的优雅语法,例如:
0 <= n < 256
的含义,
0 <= n and n < 256
在语法上知道它是一种相当灵活的语言,是否可以在Scala中模拟这个功能?
答案 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
您可以将其用于任何有序类型,例如Int
或Double
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!