Answers:
它用于顺序理解(例如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
效果不同。
我认为已接受的答案很好,但是似乎许多人未能掌握一些基本要点。
首先,Scala的for
理解力等同于Haskell的理解力do
表达,它不过是组成多个单子运算的语法糖。由于该语句很可能无法帮助需要帮助的人,因此请重试…:-)
Scala的for
理解是使用map flatMap
和组成多个操作的语法糖filter
。或者foreach
。Scala实际上将for
-expression转换为对这些方法的调用,因此提供它们的任何类或它们的子集都可以用于理解。
首先,让我们谈谈翻译。有很简单的规则:
这个
for(x <- c1; y <- c2; z <-c3) {...}
被翻译成
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
这个
for(x <- c1; y <- c2; z <- c3) yield {...}
被翻译成
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
这个
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
则回退到前者,但方法是可用的。请参阅以下部分以了解更多信息。
这个
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
,并返回赔率列表-因为found
是false
。只有这样才能foreach
执行,但是此时,更改found
已经没有意义filter
了。
在的情况下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
按需考虑,而不是事先将其应用于整个收藏。
引入了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之间从非严格更改为严格。
withFilter
即使对于严格的集合,它也应该是非严格的,这值得解释。我会考虑的...
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 => {...})))
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...})
或我缺少什么?
是的,正如Earwicker所说,它与LINQ差不多,并且select
与Ruby和Python几乎没有关系yield
。基本上,在C#中您应该写在哪里
from ... select ???
在Scala中,您改为
for ... yield ???
同样重要的是要理解-理解for
不仅适用于序列,而且适用于定义某些方法的任何类型,就像LINQ一样:
map
,则它允许for
由单个生成器组成的-expressions。flatMap
了和map
,则它允许for
由多个生成器组成的-expressions。foreach
,则它允许for
-loops没有屈服(带有单个和多个生成器)。filter
,它允许for
开始与-filter表达式if
中for
表达。除非您从Scala用户那里得到更好的答案(我不是),否则这是我的理解。
它仅显示为以开头的表达式的一部分,该表达式for
说明如何从现有列表生成新列表。
就像是:
var doubled = for (n <- original) yield n * 2
因此,每个输入都有一个输出项(尽管我相信有一种删除重复项的方法)。
这与其他语言中yield启用的“命令式延续”截然不同,后者提供了一种从几乎任何结构的命令式代码生成任何长度的列表的方法。
(如果您熟悉C#,它比LINQ的 select
运算符更接近yield return
)。
yield
Scala中的关键字只是语法糖,可以很容易地用a代替map
,正如Daniel Sobral已经详细解释的那样。
另一方面,yield
如果您正在寻找类似于Python中的生成器(或延续),绝对会产生误导。有关更多信息,请参见此SO线程:在Scala中实现“ yield”的首选方法是什么?
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))
希望这可以帮助!!
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 )
yield将输出类似:List(5,6)的结果,这很好
而map()会返回类似:List(false,false,true,true,true)的结果,这可能不是您想要的。