为啥必须在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))
但这仅适用于 <-
左侧没有模式匹配的简单 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入门到精通——第十五节 Case Class与模式匹配
为啥这个 const auto 变量在 range-for 循环中为类的 const 成员函数编译?