【问题标题】:How does the extractor work when the `unapply` function returns Boolean instead of Option?当 `unapply` 函数返回 Boolean 而不是 Option 时,提取器如何工作?
【发布时间】:2016-04-15 02:52:39
【问题描述】:

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

    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 函数返回布尔值而不是选项时,提取器如何工作? &amp;Accepts.Xml 在这里如何工作?

【问题讨论】:

    标签: scala pattern-matching extractor partial-functions


    【解决方案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()
    }
    

    【讨论】:

      【解决方案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"
        }
      }
      

      在阅读了一些关于楼梯书的旧笔记之后,我对这一点有了更清晰的理解。案例类是一个提取器工厂。

      【讨论】:

      • 提取器需要在伴随对象中定义,而不是类本身。
      • 回到您的 Play 示例,它似乎用于指定您要接受哪些 MIME,以及您要如何处理它们。
      • 我稍微改写了你的例子并发布在github上:gist.github.com/Phil-Ba/…
      • @PhilBa 有趣的是,Play 的源代码似乎在类本身中定义了提取器。这样做有什么害处?
      • @PhilBa 请参阅 scala.util.matching.Regex 的文档以获取模式匹配示例,其中提取器只是一些实例。 SLS 8.1.8 说 StableID 是必需的,这就是为什么您不能在 case 子句中创建提取器对象,例如 case createX(15)() =&gt;
      猜你喜欢
      • 1970-01-01
      • 2018-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-21
      相关资源
      最近更新 更多