我了解Ruby和Python的产量。斯卡拉的产量是多少?
我认为公认的答案是伟大的,但似乎许多人没有抓住一些基本点。
首先,scala的for理解相当于haskell的do表示法,它只不过是组成多个单元运算的语法糖。因为这句话很可能不会帮助任何需要帮助的人,我们再试一次…:-)
scala对for的理解是用map、flatMap和filter组成多个操作的语法糖。或foreach。scala实际上将for表达式转换为对这些方法的调用,因此提供这些方法的任何类或其中的一个子集都可以用于理解。
首先,我们来谈谈翻译。有非常简单的规则:
这个
1
| for(x <- c1 ; y <- c2 ; z <-c3 ) {... } |
被翻译成
1
| c1.foreach(x => c2.foreach(y => c3.foreach(z => {...}))) |
这个
1
| for(x <- c1 ; y <- c2 ; z <- c3 ) yield {... } |
被翻译成
1
| c1.flatMap(x => c2.flatMap(y => c3.map(z => {...}))) |
这个
在scala 2.7中转换为
1
| c.filter(x => cond).map(x => {...}) |
或者,在scala 2.8中,
1
| c.withFilter(x => cond).map(x => {...}) |
如果方法withFilter不可用,但filter可用,则返回前者。有关更多信息,请参阅下面的部分。
这个
被翻译成
1
| c.map(x => (x, ...)).map((x,y) => {...}) |
当你看到非常简单的for理解时,map/foreach替代方案看起来确实更好。但是,一旦开始编写它们,就很容易在括号和嵌套级别中丢失。当这种情况发生时,for的理解通常更清楚。
我将举一个简单的例子,故意省略任何解释。您可以决定哪种语法更容易理解。
1
| l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length)) |
或
1 2 3 4 5
| for {
sl <- l
el <- sl
if el > 0
} yield el. toString. length |
withFilter
scala 2.8引入了一个名为withFilter的方法,其主要区别在于,它不返回新的、经过过滤的集合,而是按需过滤。filter方法的行为是根据集合的严格性定义的。为了更好地理解这一点,让我们来看一些使用EDOCX1(严格)和EDOCX1(非严格)的scala 2.7:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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影响它。为了说明这一点,以下是理解代码的等价物:
1 2 3 4 5
| 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,它总是不严格的,不管收集的严格程度如何。下面的示例显示了在scala 2.8上使用两种方法的List:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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的行为方式。另一方面,在scala 2.7和scala 2.8之间,Range从非严格改为严格。
- 在scala 2.8中有一种新的带过滤器的方法。对于(x<-c;if cond),在scala2.8中,yield…转换为c.withfilter(x=>cond).map(x=>…)。
- @东太阳是真的,虽然也有自动回退。withFilter也应该是不严格的,即使对于严格的托收也是如此,这应该得到一些解释。我会考虑这个…
- @丹尼尔:奥德斯基(Odersky)等人的《scala中的编程》(Programming in scala)中对这个主题有很好的论述。(我相信你已经知道了)。+1.用于展示。
- 前2点正确: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 => {...})))。
- 这个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...})还是我遗漏了什么?
- @安德鲁·马歇尔编辑得很好!我从没想过用8个空格来对齐列表中的代码部分。你错过了一个双引号。;)
它被用于序列理解(例如Python的列表理解和发生器,你可以使用yield)。
它应用于与for的组合,并将一个新的元素写入结果序列。
简单的例子(选自scala-lang)
1 2 3 4 5 6 7
| /** 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)
}
} |
对应表达式
ZZU1
黄金
1
| from a in args select a.toUpperCase |
成人关系
Ruby's yieldhas a different effect.
- 那么我为什么要用产量而不是地图呢?这个地图代码等价于val res=args.map(.touppercase),对吗?
- 以防您更喜欢这种语法。此外,正如Alexey指出的,理解还为访问flatmap、filter和foreach提供了很好的语法。
- 我个人更喜欢args map touppercase语法,因为它"感觉"更多的是OO。它似乎更符合scala的目标,即通过库支持交付仅通过定制的构造和关键字在其他语言中可用的内容。
- 正确的。如果您只有一个简单的映射——一个没有if的生成器——我肯定会说调用映射更可读。如果您有几个相互依赖的生成器和/或筛选器,您可能更喜欢for表达式。
- 请注意,给出的示例与map表达式不同:它是相同的。a for understruction被转换为对map、flatmap和filter的调用。
- 我想我明白了:val unorderedcousses=assessforsymbol.map case(symbol,assesses)=>secretsforsymbol.getorelse(symbol,0)min(assesses)不确定它能带来多少好处。
- 在这种情况下,这是正确的,但它实际上是一个更一般的结构,请参阅最乐观的答案(@tempus imho您应该考虑接受它)。
- 答案是这样开始的:"它用于序列理解(比如python的列表理解和生成器,在这里您也可以使用yield)。"这错误地导致人们认为scala中的yield类似于python中的yield。事实并非如此。在Python中,yield用于协程(或continuations)的上下文中,而scala中则不是这样。如需更多说明,请访问此线程:stackoverflow.com/questions/2201882/&hellip;
- 在我学习scala的时候,我不明白为什么需要先绘制平面图,然后再绘制平面图,但是如果没有它提供的便利,我就无法继续下去。只需尝试执行List(List(1, 2), List(3,4,5), List(6)).flatMap(identity),它将显示惊人的地图减少或按此提供分组。产量扩大到flatMap或withFilter (cond) map(func),产量不扩大到foreach。
是的,正如厄尔维克所说,这是一个很好的等值链接,它的select和鲁比和Python的yield。基本上,你会写在哪里
在你之前
了解for的另一个重要方面是,理解不只是一个序列,而是一个确定某些方法的类型,就像Linq:
- 如果你的类型的定义是单一发生器
- 如果它的定义与map一样,它允许for-表达式由几种发电机
- 如果它的定义是foreach,它允许for无产量的LOOOPS(两者兼有单个和多个发生器)。
- 如果它的定义是filter,它允许for过滤器表达式以if开始在for中
- 在C的LINQ中,正确的顺序是"从……"选择???"
- @Eldritch难题-有趣的是,它与原始SQL规范的概述顺序相同。沿着这条路的某个地方,SQL语言颠倒了顺序,但首先描述从中提取的内容,然后描述期望从中获得的内容是完全有意义的。
除非你有一个更好的答案从一个大型用户(我不知道),这就是我的理解。
It only appears as part of an expression beginning with for,which States how to generate a new list from an existing list.
像这样的事
因此,每个输入都有一个输出项目(尽管我相信有一个吸收复制品的方法)。
这与其他语言中的"imperative continuctions"不同,因为它提供了一种方法来产生一个长度的列表,从某种imperative code with almost any structure。
(如果你熟悉C 35;)
- 它应该是"var double=for(n<-original)yield n*2"。
scala中的关键字yield只是简单的句法糖,可以很容易地用map替换,正如DanielSobral已经详细解释过的那样。
另一方面,如果您在寻找类似于Python中的生成器(或延续),那么yield绝对是一种误导。有关更多信息,请参阅这个so线程:在scala中实现"yield"的首选方法是什么?
型
考虑以下内容以便理解
1
| val A = for (i <- Int. MinValue to Int. MaxValue; if i > 3) yield i |
按如下方式大声读出可能会有所帮助
对于每个整数i,如果它大于3,则生成(产生)i,并将其添加到列表A中。
就数学集合生成器符号而言,上述理解类似于
。
可以理解为
对于每个整数,如果它大于,则它是集合的成员。
或者作为
是所有整数的集合,使每个大于。
型
yield类似于for循环,它有一个我们看不到的缓冲区,对于每个增量,它会不断向缓冲区添加下一个项。当for循环完成运行时,它将返回所有生成值的集合。yield可以用作简单的算术运算符,甚至可以与数组组合使用。下面是两个简单的例子,您可以更好地理解
号
res:scala.collection.immutable.indexedseq[int]=矢量(3、6、9、12、15)
1 2 3 4 5 6 7 8 9 10
| 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)]=列表((1,a),(1,b),(1,c),(2,a),(2,b),(2,c),(3,a),(3,b),(3,c)
希望这有帮助!你看!
- 在回答一个这么老的问题(9年前)时,指出你的答案与已经提交的所有其他答案有何不同是很有帮助的。
- 我认为澄清疑问很重要,不要给出不同的答案,因为即使我也是学习这门语言的初学者。谢谢你的建议。
1 2 3 4 5 6 7
| 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 ) |
这两段代码是等效的。
1 2 3 4 5
| val res3 = for (al <- aList ) yield al + 1 > 3
val res4 = aList. map( _+ 1 > 3 )
println ( res3 )
println ( res4 ) |
这两段代码也是等效的。
地图和产量一样灵活,反之亦然。
yield比map()更灵活,请参见下面的示例
1 2 3 4 5 6 7
| 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),这可能不是您想要的结果。
- 这种比较是错误的。你在比较两种不同的东西。yield中的表达式决不会与map中的表达式做相同的事情。此外,与MAP相比,它根本没有显示收益率的"灵活性"。
- 产量al+1>3也会有同样的效果。