简体   繁体   English

用于编写链式比较的scala中的DSL,例如<b <= c

[英]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 ,所以它根本不比较bc

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. 为了允许所有类似的类型,例如DoubleInt或自定义类型,很难使我的示例更通用。 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 ,不必评估剩余的比较。

    • This becomes tricky when you also add boolean operators, such as || 当您还添加布尔运算符(例如||时,这会变得棘手 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM