斯卡拉的收益是多少?

我了解Ruby和Python的收益。 斯卡拉的收益率是多少?

它被用于序列综合 (比如Python的list-comprehensions和generator,你也可以使用yield )。

它与for组合使用for并在结果序列中写入一个新元素。

简单的例子(从斯卡拉朗 )

 /** Turn command line arguments to uppercase */ object Main { def main(args: Array[String]) { val res = for (a <- args) yield a.toUpperCase println("Arguments: " + res.toString) } } 

F#中的对应表达式是

 [ for a in args -> a.toUpperCase ] 

要么

 from a in args select a.toUpperCase 

在Linq。

Ruby的yield有不同的影响。

我认为已经接受的答案很好,但似乎很多人没有把握一些根本的观点。

首先,斯卡拉的“理解”等同于哈斯克尔的“做”符号,它不过是一个构成多元一元运算的语法糖。 由于这种说法很可能不会帮助任何需要帮助的人,让我们再试一次… 🙂

Scala的“for comprehensions”是用于map,flatMap和filter的多重操作组合的语法糖。 或者是foreach。 Scala实际上将for-expression转换为对这些方法的调用,所以提供它们的任何类或它们的子集都可以用于理解。

首先,我们来谈谈翻译。 有非常简单的规则:

1)这个

 for(x <- c1; y <- c2; z <-c3) {...} 

被翻译成

 c1.foreach(x => c2.foreach(y => c3.foreach(z => {...}))) 

2)这个

 for(x <- c1; y <- c2; z <- c3) yield {...} 

被翻译成

 c1.flatMap(x => c2.flatMap(y => c3.map(z => {...}))) 

3)这个

 for(x <- c; if cond) yield {...} 

被翻译成Scala 2.7

 c.filter(x => cond).map(x => {...}) 

或者,在Scala 2.8上

 c.withFilter(x => cond).map(x => {...}) 

如果方法withFilter不可用,但是filter是可用的,则回withFilter前者。 有关更多信息,请参阅下面的编辑。

4)这个

 for(x <- c; y = ...) yield {...} 

被翻译成

 c.map(x => (x, ...)).map((x,y) => {...}) 

当你看起来非常简单的理解,地图/ foreach替代看起来,确实,更好。 一旦你开始编写它们,你可以很容易迷失在括号和嵌套层次。 当发生这种情况时,理解通常要清晰得多。

我会举一个简单的例子,故意省略任何解释。 你可以决定哪个语法更容易理解。

 l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length)) 

要么

 for{ sl <- l el <- sl if el > 0 } yield el.toString.length 

编辑

Scala 2.8引入了一个名为withFilter的方法,主要区别在于,它不是返回一个新的过滤集合,而是按需过滤。 filter方法根据集合的严格性定义其行为。 为了更好地理解这一点,我们来看看一些Scala 2.7的List (strict)和Stream (non-strict):

 scala> var found = false found: Boolean = false scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) 1 3 7 9 scala> found = false found: Boolean = false scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) 1 3 

发生这种差异的原因是过滤器立即应用List ,返回一系列的可能性 – 因为foundfalse 。 只有这样, foreach才会被执行,但是到了这个时候,如果filter已经执行了,那么found就是没有意义的。

Stream的情况下,条件不是立即应用的。 相反,由于每个元素都被foreach请求, filter测试条件,这使得foreach可以通过found来影响它。 为了清楚起见,下面是等同的理解代码:

 for (x <- List.range(1, 10); if x % 2 == 1 && !found) if (x == 5) found = true else println(x) for (x <- Stream.range(1, 10); if x % 2 == 1 && !found) if (x == 5) found = true else println(x) 

这就造成了很多问题,因为人们预计if被视为按需,而不是预先应用到整个收藏。

不管采集的严格程度withFilter ,Scala 2.8引入了withFilter总是非严格的。 以下示例显示Scala 2.8上的两种方法的List

 scala> var found = false found: Boolean = false scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) 1 3 7 9 scala> found = false found: Boolean = false scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) 1 3 

这产生了大多数人期望的结果,而不会改变filter行为。 作为一个方面说明, Range从非严格改为Scala 2.7和Scala 2.8。

是的,就像Earwicker所说的那样,它几乎等同于LINQ的select而与Ruby和Python的yield几乎没有关系。 基本上,你会写C#的地方

 from ... select ??? 

在斯卡拉你有相反的

 for ... yield ??? 

理解这一点也很重要, for -comprehensions不仅适用于序列,而且还适用于定义某些方法的任何类型,就像LINQ一样:

  • 如果你的类型定义了map ,它允许由单个生成器组成的表达式。
  • 如果它定义了flatMapmap ,它允许包含多个生成器的表达式。
  • 如果它定义了foreach ,它允许没有收益的循环(包括单个和多个生成器)。
  • 如果它定义了filter ,则允许在for表达式中以if开头的filter表达式。

除非你从Scala用户(我不是)得到更好的答案,这是我的理解。

它只是作为以for开头的表达式的一部分出现,它表示如何从现有列表生成新列表。

就像是:

 var doubled = for (n <- original) yield n * 2 

所以每个输入都有一个输出项(尽管我相信有一种丢弃重复项的方法)。

这与其他语言的yield所产生的“命令性延续”是完全不同的,在其中提供了一种从几乎任何结构的命令性代码生成任意长度的列表的方法。

(如果您熟悉C#,则与LINQ的 select操作符相比更接近于yield return )。

斯卡拉的关键字yield是简单的语法糖 ,可以很容易地被map取代, Daniel Sobral已经详细解释过了。

另一方面, 如果您正在寻找类似于Python中的生成器(或继续), yield是完全误导的 。 有关更多信息,请参见此线程: 在Scala中实现“yield”的首选方法是什么?

考虑下面的理解

 val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i 

如下大声读出它可能会有所帮助

对于每一个整数i如果它大于3 ,然后屈服 (产生) i并将其添加到列表A

在数学建造者符号方面 ,上面的理解是类似的

设置符号

这可能被解读为

对于每个整数 一世 如果它大于 3 ,那么它该集合的成员 一个 “。

或者作为替代

一个 是所有整数的集合 一世 ,这样每个 一世 大于 3 “。

 val aList = List( 1,2,3,4,5 ) val res3 = for ( al <- aList if al > 3 ) yield al + 1 val res4 = aList.filter(_ > 3).map(_ + 1) println( res3 ) println( res4 ) 

这两段代码是等价的。

 val res3 = for (al <- aList) yield al + 1 > 3 val res4 = aList.map( _+ 1 > 3 ) println( res3 ) println( res4 ) 

这两个代码也是等价的。

地图与收益率一样灵活,反之亦然。

yield比map()更灵活,参见下面的例子

 val aList = List( 1,2,3,4,5 ) val res3 = for ( al <- aList if al > 3 ) yield al + 1 val res4 = aList.map( _+ 1 > 3 ) println( res3 ) println( res4 ) 

产量会打印结果如:列表(5,6),这是很好的

而map()将返回如下结果:List(false,false,true,true,true),这可能不是你想要的。