当`unapply`函数返回Boolean而不是Option时,提取器如何工作?

时间:2016-01-10 20:55:30

标签: scala pattern-matching extractor partial-functions

我从楼梯书中了解了提取器:

    object Twice {
      def apply(x: Int) = x * 2
      def unapply(x: Int) = if(x % 2 == 0) Some(x / 2) else None
    }
    // outside pattern mathcing, Twice.apply(21) is called
    val x = Twice(21)
    x match {
        // inside pattern matching, Twice.unapply(x) is called,
        // the result Some(21) is matched against y,
        // y gets the value 21
      case Twice(y) => println(x + " is twice " + y)
      case _ => println(x + " is odd.")
    }

这非常直截了当。但今天我从一些关于Play框架的书中读到了这段代码:

trait RequestExtractors extends AcceptExtractors {

  //Convenient extractor allowing to apply two extractors. 
  object & { 
    def unapply(request: RequestHeader): Option[(RequestHeader, RequestHeader)] = Some((request, request)) 
  } 

}

//Define a set of extractors allowing to pattern match on the Accept HTTP header of a request 
trait AcceptExtractors {

  //Common extractors to check if a request accepts JSON, Html, etc. 
  object Accepts { 
    import play.api.http.MimeTypes 
    val Json = Accepting(MimeTypes.JSON) 
    val Html = Accepting(MimeTypes.HTML) 
    val Xml = Accepting(MimeTypes.XML) 
    val JavaScript = Accepting(MimeTypes.JAVASCRIPT) 
  } 

}

//Convenient class to generate extractors checking if a given mime type matches the Accept header of a request. 
case class Accepting(val mimeType: String) {
  def unapply(request: RequestHeader): Boolean = request.accepts(mimeType) 
  def unapply(mediaRange: play.api.http.MediaRange): Boolean = mediaRange.accepts(mimeType) 
}


def fooBar = Action { 
    implicit request => 
      val xmlResponse: Node = <metadata> 
        <company>TinySensors</company> 
        <batch>md2907</batch> 
      </metadata> 

      val jsonResponse = Json.obj("metadata" -> Json.arr( 
        Json.obj("company" -> "TinySensors"), 
        Json.obj("batch" -> "md2907")) 
      ) 

      render { 
        case Accepts.Xml() => Ok(xmlResponse) 
        case Accepts.Json() & Accepts.JavaScript() => Ok(jsonResponse) 
      }
  }

unapply函数返回Boolean而不是Option时,提取器如何工作? &Accepts.Xml如何在这里工作?

2 个答案:

答案 0 :(得分:1)

我可以告诉你关于play框架的信息,但如果在模式匹配中使用,则返回布尔值的提取器表示模式是否匹配。因此,如果提取器返回true,则表示该模式与该值匹配。这是关于提取器的一个很好的链接,也涵盖了这种情况: http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html

通常,您将提取器用于两个用例:

1)描述一个对象,这意味着返回一个或多个代表给定对象状态的值

2)在模式匹配期间,您还可以使用提取器将对象转换为另一种对象。我为这个案例做了一个小例子:

class Division(val number: Int) {
}

object Division {
    def unapply(divider: Division): Boolean = divider.number != 0

    def unapply(divider: Int): Option[Division] = if (divider != 0) Some(new Division(divider)) else None
}

val divident = 15
val divider = 5
val y = divider match {
    case Division(notZero) => divident / notZero.number //notZero is of type Division
    case _ => throw new IllegalArgumentException()
}

答案 1 :(得分:0)

好的,我找到了一种方法,通过一个最小的例子来解决这个问题:

object Unapply {

  case class DividedBy(val number: Int) {
    def unapply(divider: Int): Boolean = number % divider == 0
    def unapply(divider: Double): Boolean = number % divider.toInt == 0
  }

  val x = DividedBy(15)
  // y should be true
  val y = 5 match {
  //    case DividedBy(15)() => true
    case x() => true
    case _ => false
  }
}

奇怪的是,当你使用DividedBy(15)()(上面已注释掉)时,代码将无法编译。

更新

object Unapply {
  case class Division(val number: Int) {
//    def unapply(divider: Int): Boolean = number % divider == 0
    def unapply(divider: Int): Option[(Int, Int)] = if (number % divider == 0) Some(number/divider, 0) else None
    def unapply(divider: Double): Boolean = number % divider.toInt == 0
  }

  object Division {
    def apply(number: Int) = new Division(number)
  }

  val divisionOf15 = Division(15)
  // y should be true
  val y = 5 match {
  //    case DividedBy(15)() => true
    case divisionOf15(z, w) => s"$z, $w"
    case _ => s"Not divisible"
  }

  val z = 5.0 match {
    case divisionOf15() => "Divisible"
    case _ => "Not divisible"
  }
}

在读完楼梯书上的一些旧笔记后,我对此有了更清楚的认识。案例类是一个提取器工厂。