Scala的产量是多少?


Answers:


205

它用于顺序理解(例如Python的列表理解和生成器,您也可以在其中使用yield)。

它与组合应用,for并将新元素写入结果序列。

简单示例(来自scala-lang

/** 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效果不同。


57
那么,为什么要使用yield而不是map?此地图代码等效于val res = args.map(_。toUpperCase),对吗?
地理

4
如果您更喜欢语法。另外,正如alexey所指出的,理解还为访问flatMap,filter和foreach提供了很好的语法。
内森·史弗利·桑德斯

22
对。如果您只有一张简单的地图-一个没有if的生成器-我肯定会说调用地图更具可读性。如果您有多个相互依赖的生成器和/或过滤器,则可能更喜欢使用for表达式。
Alexey Romanov 2009年

13
请注意,给出的示例不等同于地图表达式:它是相同的。for comprehension转换为对map,flatMap和filter的调用。
Daniel C. Sobral

9
答案是这样的:“它用于序列理解(例如Python的列表理解和生成器,您也可以在其中使用yield”)。这错误地导致人们认为Scala中的yield类似于Python中的yield。不是这种情况。在Python中,yield用于协程(或延续)的上下文中,而Scala中不是这种情况。更多的澄清,请访问这个线程:stackoverflow.com/questions/2201882/...
理查德·戈麦斯

817

我认为已接受的答案很好,但是似乎许多人未能掌握一些基本要点。

首先,Scala的for理解力等同于Haskell的理解力do表达,它不过是组成多个单子运算的语法糖。由于该语句很可能无法帮助需要帮助的人,因此请重试…:-)

Scala的for理解是使用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则回退到前者,但方法是可用的。请参阅以下部分以了解更多信息。

  4. 这个

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

    被翻译成

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

当您查看非常简单的for理解时,map/ foreach替代品的确更好。但是,一旦开始编写它们,您就很容易迷失在括号和嵌套级别中。发生这种情况时,for理解通常会更加清晰。

我将展示一个简单的示例,并故意省略任何解释。您可以决定哪种语法更容易理解。

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

withFilter

Scala 2.8引入了一种称为的方法withFilter,其主要区别在于,它无需返回新的经过过滤的集合,而是按需过滤。该filter方法的行为基于集合的严格性来定义。为了更好地理解这一点,让我们看一下带有List(严格)和Stream(非严格)的Scala 2.7 :

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

之所以会出现差异,filter是因为立即应用List,并返回赔率列表-因为foundfalse。只有这样才能foreach执行,但是此时,更改found已经没有意义filter了。

在的情况下Stream,不会立即应用该条件。而是,当要求每个元素时foreachfilter测试条件,从而可以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按需考虑,而不是事先将其应用于整个收藏。

引入了Scala 2.8 withFilter,无论集合的严格性如何,它始终是非严格的。以下示例显示List了Scala 2.8的两种方法:

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之间从非严格更改为严格。


2
Scala 2.8中有一个带filter的新方法。for(x <-c; if cond)yield {...}在scala2.8中转换为c.withFilter(x => cond).map(x => {...})。
Eastsun

2
@Eastsun确实,尽管也有自动回退。withFilter即使对于严格的集合,它也应该是非严格的,这值得解释。我会考虑的...
Daniel C. Sobral

2
@Daniel:Odersky等人在“ Scala编程”中对这个主题有很好的处理。(我确定您已经知道了)。+1显示它。
拉尔夫

前两点是正确的:1. for(x <- c; y <- x; z <-y) {...}被翻译成c.foreach(x => x.foreach(y => y.foreach(z => {...}))) 2. for(x <- c; y <- x; z <- y) yield {...}被翻译成c.flatMap(x => x.flatMap(y => y.map(z => {...})))
Dominik

for(x <- c; y = ...) yield {...}真的翻译成c.map(x => (x, ...)).map((x,y) => {...})吗?我认为它已翻译成c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})或我缺少什么?
prostynick '16

23

是的,正如Earwicker所说,它与LINQ差不多,并且select与Ruby和Python几乎没有关系yield。基本上,在C#中您应该写在哪里

from ... select ??? 

在Scala中,您改为

for ... yield ???

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

  • 如果您的类型定义了just map,则它允许for由单个生成器组成的-expressions。
  • 如果同时定义flatMap了和map,则它允许for由多个生成器组成的-expressions。
  • 如果定义foreach,则它允许for-loops没有屈服(带有单个和多个生成器)。
  • 如果它定义filter,它允许for开始与-filter表达式iffor表达。

2
@Eldritch Conundrum-有趣的是,原始SQL规范概述的顺序是相同的。SQL语言在某种程度上颠倒了顺序,但是首先描述您要从中提取的内容,然后描述您希望从中获得的结果是完全有意义的。
乔丹·帕默

13

除非您从Scala用户那里得到更好的答案(我不是),否则这是我的理解。

它仅显示为以开头的表达式的一部分,该表达式for说明如何从现有列表生成新列表。

就像是:

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

因此,每个输入都有一个输出项(尽管我相信有一种删除重复项的方法)。

这与其他语言中yield启用的“命令式延续”截然不同,后者提供了一种从几乎任何结构的命令式代码生成任何长度的列表的方法。

(如果您熟悉C#,它比LINQ的 select运算符更接近yield return)。


1
它应该是“ var doubled = for(n <-original)yield n * 2”。
罗素·杨


11

考虑以下理解

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

如下朗读可能会有所帮助

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

用数学上的构建者表示法,上面的理解类似于

设定符号

可以读成

对于每个整数一世如果它大于3,则它集合的成员一个。”

或者作为

一个是所有整数的集合一世,使得每个一世大于3”。


2

Yield类似于for循环,它具有一个我们看不到的缓冲区,并且对于每个增量,它都会不断向缓冲区添加下一项。当for循环完成运行时,它将返回所有产生的值的集合。Yield可以用作简单的算术运算符,甚至可以与数组结合使用。这是两个简单的示例,供您更好地理解

scala>for (i <- 1 to 5) yield i * 3

res:scala.collection.immutable.IndexedSeq [Int] = Vector(3,6,9,12,15)

scala> val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)

scala> val letters = Seq('a', 'b', 'c')
letters: Seq[Char] = List(a, b, c)

scala> val res = for {
     |     n <- nums
     |     c <- letters
     | } yield (n, c)

res:Seq [(Int,Char)] = List((1,a),(1,b),(1,c),(2,a),(2,b),(2,c),( 3,a),(3,b),(3,c))

希望这可以帮助!!


回答这个古老的问题(超过9年)时,指出您的答案与已经提交的所有其他答案有何不同是很有帮助的。
jwvh

我认为弄清疑问很重要,不要给出不同的答案,因为即使我也是学习这种语言的初学者。谢谢你的建议。
Manasa Chada

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

这两段代码也是等效的。

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


-3

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 )

yield将输出类似:List(5,6)的结果,这很好

而map()会返回类似:List(false,false,true,true,true)的结果,这可能不是您想要的。


4
这种比较是错误的。您正在比较两件事。yield中的表达式绝不会与map中的表达式做相同的事情。而且,与地图相比,它完全没有显示出产量的“灵活性”。
dotnetN00b 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.