2009-07-22 4 views
11

Python поддерживает изящный синтаксис для «прикованных сравнения», например:Цепные сравнения в Scala

0 <= n < 256 

смысл,

0 <= n and n < 256 

Зная, что это довольно гибкий язык синтаксически, есть возможность эмулировать эту функцию в Scala?

+1

Мэтт, я предлагаю вам принять принятый ответ Александра. Он придумал что-то намного превосходящее. –

+1

Сделали. Благодарим за ответы. Приятно, что вы можете приблизиться к этому в Scala, хотя, вероятно, он более ясен для явной версии с двумя сравнениями и «&&». –

ответ

8

ответ Даниила является всеобъемлющим , на самом деле его сложно добавить. Поскольку в его ответе представлен один из вариантов, о которых он упомянул, я хотел бы просто добавить свои 2 цента и представить очень короткое решение проблемы по-другому. Описание Даниила:

Вы могли бы, теоретически, кормить результат одного из методов в другой метод.Я могу думать о двух способов сделать это:

  • Имея < = возвращает объект, который имеет как параметр он получил и результат Comparision и имеющий < использовать оба эти значения в зависимости от обстоятельств.

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 с:

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, где я t должно быть:

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

Это реализация первого варианта, который я упомянул, и это очень умно. Продолжайте и скопируйте мое объяснение, чтобы ваш ответ был полным. Я предпочитаю то, что вы придумали, чем то, что я сделал, и если вы выполните свой ответ, я соглашусь отдать предпочтение вашему ответу. Но выберите что-то лучше, чем «r». :-) –

+0

Спасибо! Я обновил ответ. –

5

Не совсем. Нужно помнить, что, помимо нескольких ключевых слов, все в Scala - это метод invokation для объекта.

Поэтому нам нужно вызвать методы «< =» и «<» для объекта, и каждый такой метод получает параметр. Тогда вам нужно четыре объекта, и есть три явных и два неявных - результаты каждого метода.

Теоретически вы можете передать результат одного из методов другому методу. Я могу думать о двух способов сделать это:

  • < Имея = возвращает объект, который имеет как параметр он получил и результат Comparision и имеющий < использовать оба эти значения в зависимости от обстоятельств.

  • Имея < = возвращает либо ложь, либо полученный параметр, и с < либо сбой, либо сравнение с другим параметром. Это можно сделать с помощью класса «Либо» или чего-то на его основе.

Эти два решения очень похожи.

Одна из проблем заключается в том, что потребители таких операторов сравнения ожидают, в результате, булевых. На самом деле это самая простая задача для решения, поскольку вы можете определить неявное из Bitherle [Boolean, T] в Boolean.

Итак, теоретически, это возможно. Вы можете сделать это с помощью собственного класса. Но как бы вы решили изменить уже существующие методы? Знаменитый шаблон 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! 
Смежные вопросы