2016-01-10 3 views
0

я узнал о пылеудаляющих лестничной книги:Как работает экстрактор, когда функция `unapply` возвращает Boolean вместо опции?

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 вместо опции? Как сделать &, Accepts.Xml работа здесь?

ответ

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)() (комментировал выше), код не будет компилироваться.


Update:

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 - это фабрика-экстрактор.

+0

Экстракторы должны быть определены в сопутствующем объекте, а не в самом классе. – PhilBa

+0

Возвращаясь к вашему примеру воспроизведения, он, кажется, используется для указания, какие MIME вы хотите принять, и как вы хотите их обрабатывать. – PhilBa

+0

Я немного переписал ваш пример и разместил его на github: https://gist.github.com/Phil-Ba/7725cba259973834ae70#file-extractor-scala – PhilBa

1

Я могу рассказать вам о структуре воспроизведения, но если используется в шаблоне, соответствующем экстрактору, возвращающему логическое значение, это означает, соответствует ли шаблон. Таким образом, если экстрактор возвращает 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() 
} 
Смежные вопросы