Как работает экстрактор, когда функция `unapply` возвращает Boolean вместо Option?
Я узнал об экстракторах из книги на лестнице:
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 Framework этот код:
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
функция возвращает логическое значение вместо Option? Как &
, Accepts.Xml
работать здесь?
2 ответа
Хорошо, я нашел способ понять это, сделав минимальный пример:
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"
}
}
Прочитав несколько старых заметок на лестничной книжке, теперь я стал лучше понимать это. Класс Case - это экстракторная фабрика.
Я действительно могу рассказать вам об игровой платформе, но при использовании в сопоставлении с шаблоном экстрактор, возвращающий логическое значение, означает, что шаблон соответствует. Таким образом, если экстрактор возвращает 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()
}