Scala сопоставление с образцом в None и Some () в одном и том же случае - PullRequest
0 голосов
/ 27 мая 2020

Какой самый элегантный случай иметь None и Some () в одном и том же случае? Что-то вроде:

val data: Option[Int] = getSomeData()

data match {
 case None || Some(data) && data > 50 =>
 case _ =>
}

Ответы [ 2 ]

4 голосов
/ 27 мая 2020

В качестве условия можно использовать Option.forall.

def foo(data: Option[Int]): Unit =
  if (data.forall(_ > 50)) println("OK")
  else println("KO")

foo(None)
// => OK

foo(Some(1))
// => KO

foo(Some(51))
// OK
2 голосов
/ 27 мая 2020

Обычно такое сопоставление с образцом может быть записано следующим образом

data match {
  case None                    => doSomething()
  case Some(data) if data > 50 => doSomething()
  case _                       => doOther()
}

Если такая комбинация (None || Some(data) && data > 50) встречается часто, вы можете ввести собственный экстрактор

object GreaterThan50OrEmpty {
  def unapply(arg: Option[Int]): Boolean = arg match {
    case None                    => true
    case Some(data) if data > 50 => true
    case _                       => false
  }
}

data match {
  case GreaterThan50OrEmpty() => println("matches pattern")
  case _                      => println("default")
}

Вы даже можете назвать его как хотите

object `None || Some(data) && data > 50` {
  def unapply(arg: Option[Int]): Boolean = arg match {
    case None                    => true
    case Some(data) if data > 50 => true
    case _                       => false
  }
}

data match {
  case `None || Some(data) && data > 50`() => println("matches pattern")
  case _                                   => println("default")
}

Чуть более общий подход

class GreaterThanOrEmpty(dataBound: Int) {
  def unapply(arg: Option[Int]): Boolean = arg match {
    case None                           => true
    case Some(data) if data > dataBound => true
    case _                              => false
  }
}

val GreaterThan50OrEmpty = new GreaterThanOrEmpty(50)

data match {
  case GreaterThan50OrEmpty() => println("matches pattern")
  case _                      => println("default")
}

Вы даже можете сгенерировать такие unapply автоматически (хотя, думаю, это того не стоит)

import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

@compileTimeOnly("enable macro paradise")
class extractor[A] extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro ExtractorMacro.impl
}

object ExtractorMacro {
  def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._
    val typA = c.prefix.tree match {
      case q"new extractor[$a]" => a
    }
    annottees match {
      case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
        val cases = tname.decoded.split('|').map(s => s"case $s => true").mkString("\n")
        val casesTree = c.parse(
          s"""arg match {
             | $cases
             | case _ => false
             |}""".stripMargin)
        q"""$mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
          ..$body
          def unapply(arg: $typA): Boolean = $casesTree
        }"""
      case _ => c.abort(c.enclosingPosition, "not object")
    }
  }
}

Использование:

@extractor[Option[Int]]
object `Some(x) if x < 25 | None | Some(data) if data > 50`

//Warning:scalac: object ... extends scala.AnyRef {
//  ...
//  def unapply(arg: Option[Int]): Boolean = arg match {
//    case Some((x @ _)) if x.$less(25)          => true
//    case None                                  => true
//    case Some((data @ _)) if data.$greater(50) => true
//    case _                                     => false
//  }
//}

def test(arg: Any) = arg match {
  case `Some(x) if x < 25 | None | Some(data) if data > 50`() => 
    println("matches pattern")
  case _ => println("default")
}

test(None)     // matches pattern
test(Some(51)) // matches pattern
test(Some(24)) // matches pattern
test(Some(30)) // default
...