10

私はよく、2 つのオブジェクトを比較し、それらが同じか異なるかに基づいて値を生成するコードを書いています。

だから私は書くかもしれません:

val result = (v1,v2) match {
  case (Some(value1), Some(value2)) => "a"
  case (Some(value), None)) => "b"
  case (None, Some(value)) => "b"
  case _ = > "c"
}

2番目と3番目のケースは本当に同じなので、書いてみました:

val result = (v1,v2) match {
  case (Some(value1), Some(value2)) => "a"
  case (Some(value), None)) || (None, Some(value)) => "b"
  case _ = > "c"
}

しかし、運がありません。

いくつかの場所でこの問題に遭遇しましたが、これは単なる具体的な例です。より一般的なパターンは、2 つのものがあり、そのうちの 1 つだけが述語を満たしているかどうかを知りたいので、次のように書きたいと思います。このようなもの:

val result = (v1,v2) match {
  case (Some(value1), Some(value2)) => "a"
  case OneAndOnlyOne(value, v: Option[Foo] => v.isDefined ) => "b"
  case _ = > "c"
}

したがって、ここでの考え方は、OneAndOnlyOne を述語 (この場合は isDefined) で構成でき、複数の場所で使用できるということです。

上記はまったく機能しません。逆方向であるため、述語は返されずにエクストラクターに渡される必要があります。

このようなものはどうですか?

val result = (v1,v2) match {
  case (Some(value1), Some(value2)) => "a"
  case new OneAndOnlyOne(v: Option[Foo] => v.isDefined )(value) => "b"
  case _ = > "c"
}

と:

class OneAndOnlyOne[T](predicate: T => Boolean) {
  def unapply( pair: Pair[T,T] ): Option[T] = {
    val (item1,item2) = pair
    val v1 = predicate(item1)
    val v2 = predicate(item2)

    if ( v1 != v2 )
      Some( if ( v1 ) item1 else item2 )
    else
      None
  }
}

しかし、これはコンパイルされません。

誰でもこのソリューションを機能させる方法を見ることができますか? または、別の解決策を提案しますか? 私はおそらくこれを実際よりも複雑にしています:)

4

6 に答える 6

19

あなたは2つのわずかに異なる質問をしていると思います。

1 つの質問は、switch ステートメントで「または」を使用する方法です。|| 動作しません。| | します。その場合、変数を使用することはできません (一般に、それらは異なる型に一致する可能性があり、型が混乱する可能性があるためです)。そう:

def matcher[T](a: (T,T)) = {
  a match {
    case (Some(x),Some(y)) => "both"
    case (Some(_),None) | (None,Some(_)) => "either"
    case _ => "none"
  }
}

別の質問は、特にタプルの値を取得できるようにしたい場合に、これを何度も行う必要がないようにする方法です。オプションのバージョンをここに実装しましたが、ラップされていないタプルとブール値を使用できます。

これを実現するための 1 つの秘訣は、値の照合を開始する前に値を事前にラップしてから、必要な処理を行う独自の照合構造を使用することです。例えば、

class DiOption[+T] {
  def trinary = this
}
case class Both[T](first: T, second:T) extends DiOption[T] { }
case class OneOf[T](it: T) extends DiOption[T] { }
case class Neither() extends DiOption[Nothing] { }
implicit def sometuple2dioption[T](t2: (Option[T],Option[T])): DiOption[T] = {
  t2 match {
    case (Some(x),Some(y)) => Both(x,y)
    case (Some(x),None) => OneOf(x)
    case (None,Some(y)) => OneOf(y)
    case _ => Neither()
  }
}

// Example usage
val a = (Some("This"),None)
a trinary match {
  case Both(s,t) => "Both"
  case OneOf(s) => "Just one"
  case _ => "Nothing"
}
于 2010-01-11T05:04:10.417 に答える
7

任意の述語をサポートする必要がある場合は、これから導き出すことができます(これはDanielのアイデアに基づいています)。

List(v1, v2) filter (_ %2 == 0) match {
    case List(value1, value2) => "a"
    case List(value) => "b"
    case _ => "c"
}

関数の定義:

def filteredMatch[T,R](values : T*)(f : T => Boolean)(p: PartialFunction[List[T], R]) : R = 
    p(List((values filter f) :_* ))

これで、次のように使用できます。

filteredMatch(v1,v2)(_ %2 == 0){
    case List(value1, value2) => "a"
    case List(value) => "b"
    case _ => "c"
}

それが良い考えであるかどうか(つまり、読みやすいかどうか)はよくわかりません。しかし、それでもきちんとした運動。

case (value1, value2) => ...リストの代わりにタプルで一致させることができれば、それは素晴らしいことです。

于 2010-01-11T15:39:39.313 に答える
6

これはどう:

    Welcome to Scala version 2.8.0.r20327-b20091230020149 (Java HotSpot(TM) Client VM, Java 1.6.0_17).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def m(v1: Any,v2: Any) = (v1,v2) match {
     |     case (Some(x),Some(y)) => "a"
     |     case (Some(_),None) | (None,Some(_)) => "b"
     |     case _ => "c"
     | }
m: (v1: Any,v2: Any)java.lang.String

scala> m(Some(1),Some(2))
res0: java.lang.String = a

scala> m(Some(1),None)
res1: java.lang.String = b

scala> m(None,None)
res2: java.lang.String = c

scala>
于 2010-01-11T03:28:16.487 に答える
4

最初にvalとして定義すると、それを実行できるはずです。

val MyValThatIsCapitalized = new OneAndOnlyOne(v: Option[Foo] => v.isDefined )
val result = (v1,v2) match {
  case (Some(value1), Some(value2)) => "a"
  case MyValThatIsCapitalized(value) => "b"
  case _ = > "c"
}

名前が示すように、extractorオブジェクトを含むvalの名前は大文字にする必要があります。

于 2010-01-11T04:10:51.220 に答える
3

Scala 2.8 の場合:

val result = List(v1,v2).flatten match {
  case List(value1, value2) => "a"
  case List(value) => "b"
  case _ = > "c"
}

ただし、Scala 2.7 では、それを機能させるために型ヒントが必要です。たとえば、 が であると仮定するvalueと、次のようになります。Int

val result = (List(v1,v2).flatten : List[Int]) match {
  case List(value1, value2) => "a"
  case List(value) => "b"
  case _ = > "c"
}

それについて面白いのは、Mitch Blevinsの回答で「最初」を「リスト」と読み違えたことで、この考えが浮かびました。:-)

于 2010-01-11T11:15:12.320 に答える
0

すでに (Some(x), Some(y)) に対して一致しているため、(None, None) に対して明示的に一致する可能性があり、残りのケースは (Some(x), None) および (None, Some(y)) です。 :

def decide [T](v1: Option[T], v2:Option[T]) = (v1, v2) match {
  case (Some (x), Some (y)) => "a"
  case (None, None)         => "c"
  case _                    => "b"
}

val ni : Option [Int] = None 
decide (ni, ni)            // c
decide (Some (4), Some(3)) // a
decide (ni, Some (3))      // b
decide (Some (4), ni)      // b
于 2011-11-15T23:09:35.683 に答える