为啥必须在scala的for循环中为模式匹配定义过滤器?

Posted

技术标签:

【中文标题】为啥必须在scala的for循环中为模式匹配定义过滤器?【英文标题】: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).mapcase (k, v) => k -> v
res2: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4))

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

【问题讨论】:

【参考方案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).mapcase (k, v) => k -> v
scala.MatchError: 5

【讨论】:

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

以上是关于为啥必须在scala的for循环中为模式匹配定义过滤器?的主要内容,如果未能解决你的问题,请参考以下文章

Scala-流程控制

Scala入门到精通——第十五节 Case Class与模式匹配

为啥这个 const auto 变量在 range-for 循环中为类的 const 成员函数编译?

快学Scala第14章----模式匹配和样例类

如何在spring data jpa中为自定义采石场提供可变参数而不使用for循环

Python:为啥这个 for 循环在第一次迭代后退出?