什么是Scala的收益?

本文翻译自:What is Scala's yield?

I understand Ruby and Python's yield. 我理解Ruby和Python的收益率。 What does Scala's yield do? Scala的收益率是多少?


#1楼

参考:https://stackoom.com/question/4PnQ/什么是Scala的收益


#2楼

It is used in sequence comprehensions (like Python's list-comprehensions and generators, where you may use yield too). 它用于序列推导 (如Python的列表推导和生成器,您也可以使用yield )。

It is applied in combination with for and writes a new element into the resulting sequence. 它与for结合使用并将新元素写入结果序列中。

Simple example (from scala-lang ) 简单的例子(来自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)
  }
}

The corresponding expression in F# would be F#中的相应表达式将是

[ for a in args -> a.toUpperCase ]

or 要么

from a in args select a.toUpperCase 

in Linq. 在Linq。

Ruby's yield has a different effect. Ruby的yield有不同的影响。


#3楼

Unless you get a better answer from a Scala user (which I'm not), here's my understanding. 除非你从Scala用户那里得到更好的答案(我不是),这是我的理解。

It only appears as part of an expression beginning with for , which states how to generate a new list from an existing list. 它仅作为以for开头的表达式的一部分出现,其中说明了如何从现有列表生成新列表。

Something like: 就像是:

var doubled = for (n <- original) yield n * 2

So there's one output item for each input (although I believe there's a way of dropping duplicates). 所以每个输入都有一个输出项(虽然我相信有一种方法可以删除重复项)。

This is quite different from the "imperative continuations" enabled by yield in other languages, where it provides a way to generate a list of any length, from some imperative code with almost any structure. 这与其他语言中yield的“命令式延续”完全不同,它提供了一种生成任意长度列表的方法,从几乎任何结构的命令式代码开始。

(If you're familiar with C#, it's closer to LINQ's select operator than it is to yield return ). (如果你熟悉C#,那么它更接近LINQ的 select运算符而不是yield return )。


#4楼

Yes, as Earwicker said, it's pretty much the equivalent to LINQ's select and has very little to do with Ruby's and Python's yield . 是的,正如Earwicker所说,它几乎与LINQ的select相当,并且与Ruby和Python的yield几乎没有关系。 Basically, where in C# you would write 基本上,你会写C#的地方

from ... select ??? 

in Scala you have instead 在Scala,你有

for ... yield ???

It's also important to understand that for -comprehensions don't just work with sequences, but with any type which defines certain methods, just like LINQ: 同样重要的是要理解for comprehensions不仅适用于序列,而且适用于定义某些方法的任何类型,就像LINQ一样:

  • If your type defines just map , it allows for -expressions consisting of a single generator. 如果你的类型定义只是map ,它允许for由单个发电机的-expressions。
  • If it defines flatMap as well as map , it allows for -expressions consisting of several generators. 如果定义flatMap以及map ,它允许for包括几个发电机-expressions。
  • If it defines foreach , it allows for -loops without yield (both with single and multiple generators). 如果它定义foreach ,它允许for -loops而不产率(都与单个和多个发电机)。
  • If it defines filter , it allows for -filter expressions starting with an if in the for expression. 如果它定义filter ,它允许for -filter表达式开始与iffor表达。

#5楼

I think the accepted answer is great, but it seems many people have failed to grasp some fundamental points. 我认为接受的答案很好,但似乎很多人都未能掌握一些基本要点。

First, Scala's for comprehensions are equivalent to Haskell's do notation, and it is nothing more than a syntactic sugar for composition of multiple monadic operations. 首先,Scala的for内涵等同于Haskell的do记号,它无非是多一元操作组成的语法糖多。 As this statement will most likely not help anyone who needs help, let's try again… :-) 由于这个声明很可能无法帮助任何需要帮助的人,让我们再试一次...... :-)

Scala's for comprehensions is syntactic sugar for composition of multiple operations with map, flatMap and filter . Scala for理解是用于组合多个操作的语法糖,包括map, flatMapfilter Or foreach . 或者foreach Scala actually translates a for -expression into calls to those methods, so any class providing them, or a subset of them, can be used with for comprehensions. Scala实际上将for -expression转换为对这些方法的调用,因此提供它们的任何类或它们的子集都可以用于理解。

First, let's talk about the translations. 首先,我们来谈谈翻译。 There are very simple rules: 有非常简单的规则:

  1. This 这个

     for(x <- c1; y <- c2; z <-c3) {...} 

    is translated into 被翻译成

     c1.foreach(x => c2.foreach(y => c3.foreach(z => {...}))) 
  2. This 这个

     for(x <- c1; y <- c2; z <- c3) yield {...} 

    is translated into 被翻译成

     c1.flatMap(x => c2.flatMap(y => c3.map(z => {...}))) 
  3. This 这个

     for(x <- c; if cond) yield {...} 

    is translated on Scala 2.7 into 在Scala 2.7上翻译成

     c.filter(x => cond).map(x => {...}) 

    or, on Scala 2.8, into 或者,在Scala 2.8上,进入

     c.withFilter(x => cond).map(x => {...}) 

    with a fallback into the former if method withFilter is not available but filter is. 如果方法withFilter不可用但filter是,则回withFilter前者。 Please see the section below for more information on this. 有关详细信息,请参阅以下部分。

  4. This 这个

     for(x <- c; y = ...) yield {...} 

    is translated into 被翻译成

     c.map(x => (x, ...)).map((x,y) => {...}) 

When you look at very simple for comprehensions, the map / foreach alternatives look, indeed, better. 当你看到非常简单for理解时, map / foreach替代品看起来确实更好。 Once you start composing them, though, you can easily get lost in parenthesis and nesting levels. 但是,一旦开始编写它们,就很容易迷失在括号和嵌套级别中。 When that happens, for comprehensions are usually much clearer. 当发生这种情况, for内涵是通常更加清晰。

I'll show one simple example, and intentionally omit any explanation. 我将展示一个简单的例子,故意省略任何解释。 You can decide which syntax was easier to understand. 您可以决定哪种语法更容易理解。

l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))

or 要么

for {
  sl <- l
  el <- sl
  if el > 0
} yield el.toString.length

withFilter

Scala 2.8 introduced a method called withFilter , whose main difference is that, instead of returning a new, filtered, collection, it filters on-demand. Scala 2.8引入了一个名为withFilter的方法,它的主要区别在于,它不是返回一个新的,已过滤的集合,而是按需过滤。 The filter method has its behavior defined based on the strictness of the collection. filter方法的行为是根据集合的严格性定义的。 To understand this better, let's take a look at some Scala 2.7 with List (strict) and Stream (non-strict): 为了更好地理解这一点,让我们看看一些带有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

The difference happens because filter is immediately applied with List , returning a list of odds -- since found is false . 之所以出现这种差异是因为filter会立即应用List ,返回一个赔率列表 - 因为found false Only then foreach is executed, but, by this time, changing found is meaningless, as filter has already executed. 只有这样才能执行foreach ,但是,到目前为止,更改found是没有意义的,因为filter已经执行了。

In the case of Stream , the condition is not immediatelly applied. Stream的情况下,不立即应用该条件。 Instead, as each element is requested by foreach , filter tests the condition, which enables foreach to influence it through found . 相反,由于foreach请求每个元素, filter测试条件,这使foreach能够通过found来影响它。 Just to make it clear, here is the equivalent for-comprehension code: 为了说清楚,这里是等效的理解代码:

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)

This caused many problems, because people expected the if to be considered on-demand, instead of being applied to the whole collection beforehand. 这导致了许多问题,因为人们期望按需考虑if ,而不是事先应用于整个集合。

Scala 2.8 introduced withFilter , which is always non-strict, no matter the strictness of the collection. Scala 2.8引入了withFilter ,无论收集的严格程度withFilter ,它都是非严格的。 The following example shows List with both methods on Scala 2.8: 以下示例在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

This produces the result most people expect, without changing how filter behaves. 这会产生大多数人期望的结果,而不会改变filter行为方式。 As a side note, Range was changed from non-strict to strict between Scala 2.7 and Scala 2.8. 另外,在Scala 2.7和Scala 2.8之间, Range从非严格变为严格。


#6楼

The keyword yield in Scala is simply syntactic sugar which can be easily replaced by a map , as Daniel Sobral already explained in detail. Scala中的关键字yield只是语法糖 ,可以很容易地用map代替,正如Daniel Sobral已经详细解释过的那样

On the other hand, yield is absolutely misleading if you are looking for generators (or continuations) similar to those in Python . 另一方面, 如果您正在寻找类似于Python中的生成器(或延续),则yield绝对会产生误导 See this SO thread for more information: What is the preferred way to implement 'yield' in Scala? 有关更多信息,请参阅此SO线程: 在Scala中实现'yield'的首选方法是什么?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值