斯卡拉的收益是多少?
我了解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
,返回一系列的可能性 – 因为found
是false
。 只有这样, 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
,它允许由单个生成器组成的表达式。 - 如果它定义了
flatMap
和map
,它允许包含多个生成器的表达式。 - 如果它定义了
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
在数学建造者符号方面 ,上面的理解是类似的
这可能被解读为
“ 对于每个整数 如果它大于 ,那么它是该集合的成员 “。
或者作为替代
“ 是所有整数的集合 ,这样每个 大于 “。
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),这可能不是你想要的。