2013-06-19 80 views
1

我想寫Scala中執行以下操作:DSL在階寫鏈的比較如A <B <= C

def method(a: Float, b: Float, c: Float) = { 
    if(a < b <= c) { 
    ... 
    } 
} 

目前,這是無效的。事實上,a < b返回一個布爾值,爲了比較目的,它包含booleanWrapper。然後,編譯器抱怨c是類型Float而不是Boolean的,所以不比較bc可言。

是否可以使用隱含的類,方法和價值類能夠實現這一目標?

現在,我只能夠做到以下幾點:

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) { 
    ... 
    } 
} 

任何方式刪除使用標準的招數此F?

+0

可能重複[做宏使得自然鏈比較可能在Scala中?(http://stackoverflow.com/questions/13450324/do-macros-make-naturally-chained-comparisons-possible-在-階) – senia

回答

3

是的,你可以用implicits和運營商定製模擬此。 下面是一個簡單的例子:

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 

這裏是在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 

備註:

  • 可以很容易地添加更多的比較運算符,如:<=

  • 自定義運算符的用法是必需的,因爲這會強制編譯器使用上面的implicit class而不是Float的內置默認運算符。

  • 爲了允許所有可比較的類型(如Double,Int或自定義類型),很難使我的示例更通用。之所以這很難,是因爲implicit class FloatWrapper不需要進一步的implicit參數 - implicits不能嵌套。 (或者更準確地說,它們可以在語法上,但是編譯器不會爲隱式分辨率選擇它們。)

  • 作爲一種優化,您可能需要考慮添加布爾快捷鍵,即當表達式已知是false,沒有必要評估其餘的比較。

    • 當您還添加布爾運算符(例如||&&)時,這會變得棘手。的
1

這是不可能沒有明確的包裝,如果你使用標準的類型有方法<<=。如果你能提供你自己的類型,你可以做到。

def method(a:SpecialFloat, b:SpecialFloat, c:SpecialFloat) 

implicit class SpecialFloat(v:Float) extends AnyVal { 

    def < (other:Float) = ??? 
    def <= (other:Float) = ??? 
} 

這樣你就可以用你喜歡的任何方式實現這些方法。調用method(1f, 2f, 3f)會自動將它們轉換爲SpecialFloat實例(more information about the extends AnyVal)。

相關問題