Dan*_*ral 811
我认为接受的答案很好,但似乎很多人都未能掌握一些基本要点.
首先,Scala的for
理解等同于Haskell的do
符号,它只不过是构成多个monadic操作的语法糖.由于这个声明很可能无法帮助任何需要帮助的人,让我们再试一次...... :-)
Scala的for
理解是用于组合具有map的多个操作的语法糖,flatMap
和filter
.或者foreach
.Scala实际上将for
-expression转换为对这些方法的调用,因此提供它们的任何类或其子集都可以用于理解.
首先,我们来谈谈翻译.有非常简单的规则:
这个
for(x <- c1; y <- c2; z <-c3) {...}
Run Code Online (Sandbox Code Playgroud)
被翻译成
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
Run Code Online (Sandbox Code Playgroud)这个
for(x <- c1; y <- c2; z <- c3) yield {...}
Run Code Online (Sandbox Code Playgroud)
被翻译成
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
Run Code Online (Sandbox Code Playgroud)这个
for(x <- c; if cond) yield {...}
Run Code Online (Sandbox Code Playgroud)
在Scala 2.7上翻译成
c.filter(x => cond).map(x => {...})
Run Code Online (Sandbox Code Playgroud)
或者,在Scala 2.8上,进入
c.withFilter(x => cond).map(x => {...})
Run Code Online (Sandbox Code Playgroud)
如果方法withFilter
不可用,filter
则回退到前者但是.有关详细信息,请参阅以下部分.
这个
for(x <- c; y = ...) yield {...}
Run Code Online (Sandbox Code Playgroud)
被翻译成
c.map(x => (x, ...)).map((x,y) => {...})
Run Code Online (Sandbox Code Playgroud)当你看到非常简单的for
理解时,map
/ foreach
替代品看起来确实更好.但是,一旦开始编写它们,就很容易迷失在括号和嵌套级别中.当这种情况发生时,for
理解通常会更加清晰.
我将展示一个简单的例子,故意省略任何解释.您可以决定哪种语法更容易理解.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
Run Code Online (Sandbox Code Playgroud)
要么
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
Run Code Online (Sandbox Code Playgroud)
withFilter
Scala 2.8引入了一种名为的方法withFilter
,其主要区别在于,它不是返回一个新的过滤集合,而是按需过滤.该filter
方法的行为基于集合的严格性来定义.为了更好地理解这一点,让我们看看一些Scala 2.7 List
(严格)和Stream
(非严格):
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
Run Code Online (Sandbox Code Playgroud)
之所以出现差异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)
Run Code Online (Sandbox Code Playgroud)
这导致了许多问题,因为人们期望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
Run Code Online (Sandbox Code Playgroud)
这会产生大多数人期望的结果,而不会改变filter
行为方式.作为旁注,Range
Scala 2.7和Scala 2.8之间从非严格变为严格.
Dar*_*rio 204
它用于序列推导(如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)
}
}
Run Code Online (Sandbox Code Playgroud)
F#中的相应表达式将是
[ for a in args -> a.toUpperCase ]
Run Code Online (Sandbox Code Playgroud)
要么
from a in args select a.toUpperCase
Run Code Online (Sandbox Code Playgroud)
在Linq.
Ruby的yield
效果不同.
Ale*_*nov 23
是的,正如Earwicker所说,它几乎与LINQ相当,select
并且与Ruby和Python几乎没有关系yield
.基本上,你会写C#的地方
from ... select ???
Run Code Online (Sandbox Code Playgroud)
在Scala,你有
for ... yield ???
Run Code Online (Sandbox Code Playgroud)
同样重要的是要理解 - 理解for
不仅适用于序列,而且适用于定义某些方法的任何类型,就像LINQ一样:
map
,它允许 - for
表达式由单个生成器组成.flatMap
为map
,它允许 - for
由多个生成器组成的表达式.foreach
,它允许for
-loops没有yield(包括单个和多个生成器).filter
,它允许for
-filter表达式开始与if
在for
表达式.Dan*_*ker 13
除非你从Scala用户那里得到更好的答案(我不是),这是我的理解.
它仅作为以表达式开头的一部分出现for
,其中说明了如何从现有列表生成新列表.
就像是:
var doubled = for (n <- original) yield n * 2
Run Code Online (Sandbox Code Playgroud)
所以每个输入都有一个输出项(虽然我相信有一种方法可以删除重复项).
这与其他语言中yield的"命令式延续"完全不同,它提供了一种生成任意长度列表的方法,从几乎任何结构的命令式代码开始.
(如果你熟悉C#,它更接近LINQ的 select
运算符而不是它yield return
).
Ric*_*mes 12
yield
Scala中的关键字只是语法糖,可以很容易地被a替换map
,正如Daniel Sobral已经详细解释的那样.
另一方面,yield
如果您正在寻找类似于Python中的生成器(或延续),则绝对会产生误导.有关更多信息,请参阅此SO线程:在Scala中实现'yield'的首选方法是什么?
Mar*_*lic 10
考虑以下理解
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Run Code Online (Sandbox Code Playgroud)
如下大声朗读可能会有所帮助
" 对于每个整数i
,如果它是大于3
,则产率(产生)i
,并将其添加到列表中A
."
在数学集合构造符号方面,上面的理解类似于
可以读作
" 对于每个整数,如果它大于
,那么它就是该集的成员
".
或者作为
" 是所有整数的集合
,这样每个
大于
".
小智 7
Yield 类似于 for 循环,它有一个我们看不到的缓冲区,并且对于每个增量,它都会不断地将下一个项目添加到缓冲区中。当 for 循环运行结束时,它将返回所有生成值的集合。Yield 可以用作简单的算术运算符,甚至可以与数组结合使用。下面举两个简单的例子,方便大家更好的理解
scala>for (i <- 1 to 5) yield i * 3
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
res: Seq[(Int, Char)] = List((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), ( 3,a), (3,b), (3,c))
希望这可以帮助!!
归档时间: |
|
查看次数: |
116728 次 |
最近记录: |