为什么必须为Scala中for循环中的模式匹配定义过滤器? [英] why does filter have to be defined for pattern matching in a for loop in scala?

查看:115
本文介绍了为什么必须为Scala中for循环中的模式匹配定义过滤器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 阶>类C [T](项目:T *){
| def map [U](f:(T)=> U)= this.items.map(f)
| }
定义类C

scala>对于(x <新的C(1→2,3→4))产生x
res0:Seq [(Int,Int)] = ArrayBuffer((1,2),(3, 4))

但这只适用于左侧没有模式匹配的简单循环一边 < - 。如果您尝试在那里进行模式匹配,则会收到投诉,指出过滤器方法未定义:

 阶>对于((k,v)< - 新的C(1→2,3  -  4)),产生k-> v 
<控制台>:7:错误:值过滤器不是((k,v))的新的C(1 - > C)的成员C [(Int,Int)]
2,3→4))产生k→ v

为什么要在这里实现模式匹配所需的过滤器?我会认为Scala只会将上述循环转换为等价的地图调用:

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

但这似乎工作正常,所以for循环必须被翻译成别的东西。这是什么翻译成需要过滤器方法?

解决方案

简短的答案:根据Scala规范,您不需要为您给出的示例定义过滤器方法,但是有一个开放错误,这意味着它目前是必需的。



长的答案:应用于理解的desugally算法是在 Scala语言规范中描述。我们从6.19节对于理解和循环开始(我在查看规范的2.9版本):


在第一个对于e类型,每个生成器p true; case _ => false}


您的问题的重点是,理解中的模式对于给定的表达式是否无可辩驳或不。 (该模式是'< - '之前的位,表达式是稍后的)。如果它是无可辩驳的,则不会添加withFilter,否则将需要。


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



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



你的第二个例子是潜在的第三种类型的无可辩驳的模式,一种构造器模式。尝试匹配(k,v),它是 Tuple2 [Any,Any] Tuple2 [Int,Int] (参见规范中的8.1.6和8.1.7))成功,因为 Int 对于任何是无可辩驳的。因此,第二种模式也是无可辩驳的,不(不应该)需要一个 withFilter 方法。



Daniel的例子 Tuple2 [Any,Any] 对于 Any 不是无可辩驳的,所以withFilter调用被添加。 / p>

顺便提一句,错误消息讲述了一个过滤器方法,但规范谈到 withFilter - 使用Scala 2.8进行了更改,有关详细信息,请参阅此问题和答案


To create a new class that can be used in a Scala for comprehension, it seems that all you have to do is define a map function:

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))

But that only works for simple for loops where there is no pattern matching on the left hand side of <-. If you try to pattern match there, you get a complaint that the filter method is not defined:

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

Why is filter required to implement the pattern matching here? I would have thought Scala would just translate the above loop into the equivalent map call:

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

But that seems to work fine, so the for loop must be translated into something else. What is it translated into that needs the filter method?

解决方案

The short answer: according to the Scala specs, you shouldn't need to define a 'filter' method for the example you gave, but there is an open bug that means it is currently required.

The long answer: the desugaring algorithm applied to for comprehensions is described in the Scala language specification. Let's start with section 6.19 "For Comprehensions and For Loops" (I'm looking at version 2.9 of the specification):

In a first step, every generator p <- e, where p is not irrefutable (§8.1) for the type of e is replaced by p <- e.withFilter { case p => true; case _ => false }

The important point for your question is whether the pattern in the comprehension is "irrefutable" for the given expression or not. (The pattern is the bit before the '<-'; the expression is the bit afterwards.) If it is "irrefutable" then the withFilter will not be added, otherwise it will be needed.

Fine, but what does "irrefutable" mean? Skip ahead to section 8.1.14 of the spec ("Irrefutable Patterns"). Roughly speaking, if the compiler can prove that the pattern cannot fail when matching the expression then the pattern is irrefutable and the withFilter call will not be added.

Now your example that works as expected is the first type of irrefutable pattern from section 8.1.14, a variable pattern. So the first example is easy for the compiler to determine that withFilter is not required.

Your second example is potentially the third type of irrefutable pattern, a constructor pattern. Trying to match (k,v) which is Tuple2[Any,Any] against a Tuple2[Int,Int] (see section 8.1.6 and 8.1.7 from the specification)) succeeds since Int is irrefutable for Any. Therefore the second pattern is also irrefutable and doesn't (shouldn't) need a withFilter method.

In Daniel's example, Tuple2[Any,Any] isn't irrefutable against Any, so the withFilter calls gets added.

By the way, the error message talks about a filter method but the spec talks about withFilter - it was changed with Scala 2.8, see this question and answer for the gory details.

这篇关于为什么必须为Scala中for循环中的模式匹配定义过滤器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆