[英]DSL in scala to write chain comparisons such as a < b <= c
I would like to write the following in Scala: 我想在Scala中写下以下内容:
def method(a: Float, b: Float, c: Float) = {
if( a < b <= c) {
...
}
}
Currently, this is not valid. 目前,这是无效的。 Indeed,
a < b
returns a Boolean, which for comparison purposes is wrapped by a booleanWrapper
. 实际上,
a < b
返回一个布尔值,为了进行比较,它由booleanWrapper
包装。 The compiler then complains that c is of type Float
and not Boolean
, so it does not compare b
to c
at all. 编译器然后抱怨c是
Float
类型而不是Boolean
,所以它根本不比较b
到c
。
Would it be possible to use implicit classes, methods and value classes to be able to achieve this? 是否可以使用隐式类,方法和值类来实现这一目标?
For now, I have only been able to do the following: 目前,我只能做到以下几点:
class Less(val previous: Boolean, last: Float) {
def <=(other: Float): Less = new Less(previous && last <= other, other)
def <(other: Float): Less = new Less(previous && last < other, other)
}
implicit def f(v: Float): Less = {
new Less(true, v)
}
implicit def lessToBoolean(f: Less): Boolean = {
f.previous
}
def method(a: Float, b: Float, c: Float) = {
if( f(a) < b <= c) {
...
}
}
Any way to remove this f using standard tricks? 有什么方法可以使用标准技巧删除这个f?
Yes, you can emulate this with implicits and custom operators. 是的,您可以使用implicits和自定义运算符来模拟这一点。 Here is a short example:
这是一个简短的例子:
implicit class FloatWrapper(n:Float) {
def :<(that:Float) = ResultAndLastItem(n<that, that)
def :>(that:Float) = ResultAndLastItem(n>that, that)
def :=(that:Float) = ResultAndLastItem(n==that, that)
// insert more comparison operations here
}
case class ResultAndLastItem(result:Boolean, lastItem:Float) extends FloatWrapper(lastItem) {
// insert boolean operations here
// boolean operations should return another instance of ResultAndLastItem (?)
}
implicit def toBoolean(r:ResultAndLastItem):Boolean = r.result
Here is an example usage in the REPL: 以下是REPL中的示例用法:
Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_45).
Type in expressions to have them evaluated.
Type :help for more information.
scala> :paste
// Entering paste mode (ctrl-D to finish)
implicit class FloatWrapper(n:Float) {
def :<(that:Float) = ResultAndLastItem(n<that, that)
def :>(that:Float) = ResultAndLastItem(n>that, that)
def :=(that:Float) = ResultAndLastItem(n==that, that)
// insert more comparison operations here
}
case class ResultAndLastItem(result:Boolean, lastItem:Float) extends FloatWrapper(lastItem) {
// insert boolean operations here
// boolean operations should return another instance of ResultAndLastItem (?)
}
implicit def toBoolean(r:ResultAndLastItem):Boolean = r.result
// Exiting paste mode, now interpreting.
warning: there were 1 feature warnings; re-run with -feature for details
defined class FloatWrapper
defined class ResultAndLastItem
toBoolean: (r: ResultAndLastItem)Boolean
scala> if(2 :< 3 :< 4) "yay" else "nope"
res0: String = yay
scala> if(2 :< 3 :< 3) "nope" else "yay"
res1: String = yay
Remarks: 备注:
You can easily add more comparison operators, such as :<=
. 您可以轻松添加更多比较运算符,例如
:<=
。
The usage of custom operators is required because this forces the compiler to use the implicit class
above, instead of the built-in default operator for Float
s. 需要使用自定义运算符,因为这会强制编译器使用上面的
implicit class
,而不是Float
的内置默认运算符。
It is hard to make my example more generic, in order to allow for all comparable types, such as Double
, Int
, or custom types. 为了允许所有类似的类型,例如
Double
, Int
或自定义类型,很难使我的示例更通用。 The reason why this is hard is because the implicit class FloatWrapper
cannot require further implicit
arguments - implicits cannot be nested. 这很难的原因是因为
implicit class FloatWrapper
不需要进一步的implicit
参数 - implicits不能嵌套。 (Or more precisely, they can, syntactically, but then the compiler won't pick them for implicit resolution.) (或者更确切地说,它们可以在语法上,但编译器不会选择它们进行隐式解析。)
As an optimization, you might want to consider adding boolean shortcuts, ie when an expression is already known to be false
, it is not necessary to evaluate the remaining comparisons. 作为优化,您可能需要考虑添加布尔快捷方式,即,当已知表达式为
false
,不必评估剩余的比较。
||
||
时,这会变得棘手 or &&
. &&
。 This is not possible without explicit wrapping if you use standard types that have methods <
and <=
. 如果使用具有方法
<
和<=
标准类型,则无法在没有显式换行的情况下进行此操作。 If you however would provide your own types you could do it. 但是,如果您提供自己的类型,则可以执行此操作。
def method(a:SpecialFloat, b:SpecialFloat, c:SpecialFloat)
implicit class SpecialFloat(v:Float) extends AnyVal {
def < (other:Float) = ???
def <= (other:Float) = ???
}
That way you can implement the methods in any way you like. 这样你就可以以任何你喜欢的方式实现这些方法。 Calling
method(1f, 2f, 3f)
would automatically convert them into SpecialFloat
instances ( more information about the extends AnyVal
). 调用
method(1f, 2f, 3f)
会自动将它们转换为SpecialFloat
实例( 有关extends AnyVal
更多信息 )。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.