什么是Scala的收益?

Geo*_*Geo 308 functional-programming scala yield

我理解Ruby和Python的收益率.Scala的收益率是多少?

Dan*_*ral 811

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

首先,Scala的for理解等同于Haskell的do符号,它只不过是构成多个monadic操作的语法糖.由于这个声明很可能无法帮助任何需要帮助的人,让我们再试一次...... :-)

Scala的for理解是用于组合具有map的多个操作的语法糖,flatMapfilter.或者foreach.Scala实际上将for-expression转换为对这些方法的调用,因此提供它们的任何类或其子集都可以用于理解.

首先,我们来谈谈翻译.有非常简单的规则:

  1. 这个

    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)
  2. 这个

    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)
  3. 这个

    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则回退到前者但是.有关详细信息,请参阅以下部分.

  4. 这个

    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,返回赔率列表 - 因为foundfalse.只有这样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行为方式.作为旁注,RangeScala 2.7和Scala 2.8之间从非严格变为严格.

  • scala 2.8中有一个新方法withFilter.for(x < - c; if cond)yield {...}转换为scala2.8中的c.withFilter(x => cond).map(x => {...}). (2认同)
  • @Eastsun真的,虽然也有自动回退.即使对于严格的收集,`withFilter`也应该是非严格的,值得一些解释.我会考虑这个...... (2认同)
  • @Daniel:Odersky等人在"Scala编程"中对这个主题进行了很好的处理.(我相信你已经知道了).+1表示它. (2认同)

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效果不同.

  • 那么为什么我会使用yield而不是map?这个映射代码等价于val res = args.map(_.toUpperCase),对吧? (57认同)
  • 对.如果你只有一个简单的地图 - 一个没有if的生成器 - 我当然会说调用map更具可读性.如果您有多个依赖于彼此的生成器和/或过滤器,您可能更喜欢表达式. (22认同)
  • 请注意,给出的示例与地图表达式不同:它是相同的.一个for comprehension被转换为对map,flatMap和filter的调用. (13认同)
  • 答案从这样开始:"它用于序列理解(如Python的列表推导和生成器,你也可以使用yield)." 这错误地导致人们认为Scala中的收益与Python中的收益相似.不是这种情况.在Python中,yield在协同程序(或continuation)的上下文中使用,而在Scala中则不是这样.有关更多说明,请访问此主题:http://stackoverflow.com/questions/2201882/implementing-yield-yield-return-using-scala-continuations (9认同)
  • 如果您更喜欢语法.此外,正如alexey指出的那样,理解也为访问flatMap,filter和foreach提供了很好的语法. (4认同)
  • 我更喜欢args map {_ toUpperCase}语法,因为它"感觉"更多OO.它似乎更符合Scala通过库支持提供的目标,这是通过定制构造和关键字在其他语言中提供的 (2认同)
  • 在这种情况下确实如此,但它实际上是一个更通用的结构,请参阅最受欢迎的答案(@Tempus恕我直言,你应该考虑接受它). (2认同)

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表达式由单个生成器组成.
  • 如果它定义flatMapmap,它允许 - for由多个生成器组成的表达式.
  • 如果它定义foreach,它允许for-loops没有yield(包括单个和多个生成器).
  • 如果它定义filter,它允许for-filter表达式开始与iffor表达式.

  • @Eldritch难题 - 有趣的是,与原始SQL规范概述的顺序相同.在某种程度上,SQL语言颠倒了顺序,但是首先描述你从中拉出的东西然后是你希望从中获得的东西是完全合理的. (2认同)

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

yieldScala中的关键字只是语法糖,可以很容易地被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."

在数学集合构造符号方面,上面的理解类似于

设置符号

可以读作

" 对于每个整数一世,如果它大于3,那么它就是该集的成员一个".

或者作为

"一个 是所有整数的集合 一世,这样每个 一世 大于 3".


小智 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))

希望这可以帮助!!