【问题标题】:why does filter have to be defined for pattern matching in a for loop in scala?为什么必须在scala的for循环中为模式匹配定义过滤器?
【发布时间】:2011-05-21 19:37:26
【问题描述】:

要创建一个可在 Scala 中用于理解的新类,您似乎只需定义一个 map 函数即可:

scala> class C[T](items: T*) {
     |   def map[U](f: (T) => U) = this.items.map(f)
     | }
defined class C

scala> for (x <- new C(1 -> 2, 3 -> 4)) yield x
res0: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4))

但这仅适用于 &lt;- 左侧没有模式匹配的简单 for 循环。如果您尝试在那里进行模式匹配,您会收到一个投诉,即未定义 filter 方法:

scala> for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v
<console>:7: error: value filter is not a member of C[(Int, Int)]
       for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v

为什么这里需要过滤器来实现模式匹配?我原以为 Scala 只会将上述循环转换为等效的 map 调用:

scala> new C(1 -> 2, 3 -> 4).map{case (k, v) => k -> v}
res2: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4))

但这似乎可以正常工作,因此必须将 for 循环转换为其他内容。翻译成什么需要filter方法?

【问题讨论】:

    标签: scala filter map for-loop pattern-matching


    【解决方案1】:

    简短的回答:根据 Scala 规范,您不需要为您提供的示例定义“过滤器”方法,但有一个 open bug 表示当前需要它。

    长答案:用于推导式的脱糖算法在the Scala language specification 中进行了描述。让我们从第 6.19 节“For Comprehensions and For Loops”开始(我正在查看规范的 2.9 版):

    在第一步中,每个生成器 p true;案例 _ => 假 }

    您问题的重点是理解中的模式对于给定的表达式是否“无可辩驳”。 (模式是 '

    很好,但是“无可辩驳”是什么意思?跳到规范的第 8.1.14 节(“无可辩驳的模式”)。粗略地说,如果编译器能够证明模式在匹配表达式时不会失败,那么该模式是无可辩驳的,并且不会添加 withFilter 调用。

    现在您的示例按预期工作是第 8.1.14 节中第一种无可辩驳的模式,一种可变模式。所以第一个例子很容易让编译器确定withFilter不是必需的。

    您的第二个示例可能是第三种无可辩驳的模式,即构造函数模式。尝试将 Tuple2[Any,Any] 的 (k,v) 与 Tuple2[Int,Int] 匹配(请参阅规范中的第 8.1.6 和 8.1.7 节))成功,因为 Int 对于 Any 是无可辩驳的。因此,第二种模式也是无可辩驳的,不需要(不应该)withFilter 方法。

    在 Daniel 的示例中,Tuple2[Any,Any]Any 并非无可辩驳,因此添加了 withFilter 调用。

    顺便说一句,错误消息中提到了 filter 方法,但规范中提到了 withFilter - 它已在 Scala 2.8 中进行了更改,有关血腥细节,请参阅 this question and answer

    【讨论】:

      【解决方案2】:

      看看区别:

      scala> for ((k, v) <- List(1 -> 2, 3 -> 4, 5)) yield k -> v
      res22: List[(Any, Any)] = List((1,2), (3,4))
      
      scala> List(1 -> 2, 3 -> 4, 5).map{case (k, v) => k -> v}
      scala.MatchError: 5
      

      【讨论】:

      • 我检查了有无'5'的编译代码。当没有“5”时,不使用过滤器。
      • 哇,我从来没有意识到 for 循环会默默地丢弃不匹配的项目。这听起来像是一个微妙的陷阱,我最好提防。
      • map 版本几乎同样危险,因为没有警告它可能会抛出。不幸的是,目前没有语法可以在不使用偏函数的情况下解构函数文字中的参数。
      • @IttayD 这很可能是最近的优化。我想我已经看到了 paulp 对此的评论。
      • @LeoAlekseyev 在 2.10.3 上为我工作得很好。
      猜你喜欢
      • 2020-01-04
      • 1970-01-01
      • 1970-01-01
      • 2021-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-15
      相关资源
      最近更新 更多