Scala 的产量是多少?

发布于 2024-07-25 19:06:22 字数 48 浏览 4 评论 0 原文

我了解 Ruby 和 Python 的产量。 Scala的yield有什么作用?

I understand Ruby and Python's yield. What does Scala's yield do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

萤火眠眠 2024-08-01 19:06:22

我认为公认的答案很好,但似乎很多人都未能抓住一些基本点。

首先,Scala 的 for 推导式相当于 Haskell 的 do 表示法,它只不过是多个一元运算组合的语法糖。 由于这个语句很可能不会帮助任何需要帮助的人,所以让我们再试一次......:-)

Scala 的 for 推导式是使用 map、flatMap 和 组合多个操作的语法糖。过滤器。 或者foreach。 Scala 实际上将 for 表达式转换为对这些方法的调用,因此提供它们的任何类或其子集都可以与 for 推导式一起使用。

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

  1. 这个

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

    翻译成

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

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

    翻译成

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

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

    在 Scala 2.7 上被翻译成

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

    或者,在 Scala 2.8 上,进入

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

    如果 withFilter 方法不可用,但 filter 可用,则回退到前者。 有关详细信息,请参阅下面的部分。

  4. 这个

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

    翻译成

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

当你查看非常简单的 for 推导式时,map/foreach 替代方案看起来确实如此, 更好的。 然而,一旦开始编写它们,您很容易迷失在括号和嵌套级别中。 当这种情况发生时,for 理解通常会更加清晰。

我将展示一个简单的例子,并故意省略任何解释。 您可以决定哪种语法更容易理解。

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

withFilter

Scala 2.8 引入了一种名为 withFilter 的方法,其主要区别在于,它不是返回一个新的、经过过滤的集合,而是按需进行过滤。 filter 方法的行为是根据集合的严格性定义的。 为了更好地理解这一点,让我们看一下带有 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

之所以会出现差异,是因为 filter 立即与 List 一起应用,返回赔率列表 - 因为 foundfalse。 只有这样 foreach 才会执行,但是此时,更改 found 已经没有意义了,因为 filter 已经执行了。

对于Stream,条件不会立即应用。 相反,当 foreach 请求每个元素时,filter 会测试条件,这使得 foreach 能够通过 found 影响它>。 澄清一下,这里是等效的 for 理解代码:

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

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 从非严格更改为严格。

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. 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. Or 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.

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

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

    or, on Scala 2.8, into

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

    with a fallback into the former if method withFilter is not available but filter is. 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. 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.

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. The filter method has its behavior defined based on the strictness of the collection. To understand this better, let's take a look at some Scala 2.7 with List (strict) and 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

The difference happens because filter is immediately applied with List, returning a list of odds -- since found is false. Only then foreach is executed, but, by this time, changing found is meaningless, as filter has already executed.

In the case of Stream, the condition is not immediatelly applied. Instead, as each element is requested by foreach, filter tests the condition, which enables foreach to influence it through 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.

Scala 2.8 introduced withFilter, which is always non-strict, no matter the strictness of the collection. The following example shows List with both methods on 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

This produces the result most people expect, without changing how filter behaves. As a side note, Range was changed from non-strict to strict between Scala 2.7 and Scala 2.8.

预谋 2024-08-01 19:06:22

它用于 序列推导式(如 Python 的列表推导式和生成器,您可以在其中使用产量)。

它与 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)
  }
}

F# 中相应的表达式将是

[ for a in args -> a.toUpperCase ]

Linq 中的or

from a in args select a.toUpperCase 

Ruby 的 yield 有不同的效果。

It is used in sequence comprehensions (like Python's list-comprehensions and generators, where you may use yield too).

It is applied in combination with for and writes a new element into the resulting sequence.

Simple example (from 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

[ for a in args -> a.toUpperCase ]

or

from a in args select a.toUpperCase 

in Linq.

Ruby's yield has a different effect.

几度春秋 2024-08-01 19:06:22

是的,正如 Earwicker 所说,它几乎相当于 LINQ 的 select,并且与 Ruby 和 Python 的 yield 关系不大。 基本上,在 C# 中,您可以

from ... select ??? 

用 Scala

for ... yield ???

编写。同样重要的是要了解 for 推导式不仅适用于序列,还适用于定义某些方法的任何类型,就像 LINQ 一样:

  • 如果您的类型仅定义 map,则它允许包含 for 表达式
    单发电机。
  • 如果它定义了 flatMap 以及 map,则它允许 for 表达式组成
    几个发电机。
  • 如果它定义了foreach,则它允许不带yield的for循环(无论是单个生成器还是多个生成器)。
  • 如果它定义了 filter,则允许以 if 开头的 for 过滤表达式
    for 表达式中。

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. Basically, where in C# you would write

from ... select ??? 

in Scala you have instead

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:

  • If your type defines just map, it allows for-expressions consisting of a
    single generator.
  • If it defines flatMap as well as map, it allows for-expressions consisting
    of several generators.
  • If it defines foreach, it allows for-loops without yield (both with single and multiple generators).
  • If it defines filter, it allows for-filter expressions starting with an if
    in the for expression.
蓝颜夕 2024-08-01 19:06:22

除非您从 Scala 用户那里得到更好的答案(我不是),否则这是我的理解。

它仅作为以 for 开头的表达式的一部分出现,该表达式说明如何从现有列表生成新列表。

就像这样:

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

所以每个输入都有一个输出项(尽管我相信有一种删除重复项的方法)。

这与其他语言中由yield启用的“命令式延续”有很大不同,它提供了一种从具有几乎任何结构的命令式代码生成任何长度的列表的方法。

(如果您熟悉 C#,它更接近 LINQ 的 select运算符而不是yield return)。

Unless you get a better answer from a Scala user (which I'm not), here's my understanding.

It only appears as part of an expression beginning with for, which states how to generate a new list from an existing list.

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.

(If you're familiar with C#, it's closer to LINQ's select operator than it is to yield return).

傲性难收 2024-08-01 19:06:22

考虑以下内容进行理解

val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i

如下所示大声朗读可能会有所帮助

对于每个整数i如果它大于3,则产量 (产生)i并将其添加到列表A。”

在数学方面set-builder 表示法,上面的 for 理解类似于

可以读作

"For每个整数  i如果它大于3,那么它是集合 A." 的成员

"< img src="https://latex.codecogs.com/gif.latex?A" alt="A"> 是所有整数的集合 i,使得每个 i 大于 3。"

Consider the following for-comprehension

val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i

It may be helpful to read it out loud as follows

"For each integer i, if it is greater than 3, then yield (produce) i and add it to the list A."

In terms of mathematical set-builder notation, the above for-comprehension is analogous to

set-notation

which may be read as

"For each integer i, if it is greater than 3, then it is a member of the set A."

or alternatively as

"A is the set of all integers i, such that each i is greater than 3."

下壹個目標 2024-08-01 19:06:22

Scala 中的关键字 yield 只是语法糖,可以轻松地用 map 替换,如 Daniel Sobral 已经详细解释了

另一方面,如果您正在寻找类似于 yield 绝对具有误导性。 moin/Generators" rel="noreferrer">Python 中的。 请参阅此SO线程以获取更多信息: 什么是首选方法在 Scala 中实现“yield”?

The keyword yield in Scala is simply syntactic sugar which can be easily replaced by a map, as Daniel Sobral already explained in detail.

On the other hand, yield is absolutely misleading if you are looking for generators (or continuations) similar to those in Python. See this SO thread for more information: What is the preferred way to implement 'yield' in Scala?

街角卖回忆 2024-08-01 19:06:22

Yield 类似于 for 循环,它有一个我们看不到的缓冲区,并且对于每个增量,它都会不断地将下一个项目添加到缓冲区中。 当 for 循环运行结束时,它将返回所有生成值的集合。 Yield 可以用作简单的算术运算符,甚至可以与数组结合使用。
这里有两个简单的例子,可以帮助您更好地理解

scala>for (i <- 1 to 5) yield i * 3

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)

res: Seq[(Int, Char)] = List((1, a)、(1,b)、(1,c)、(2,a)、(2,b)、(2,c)、(3,a)、(3,b)、(3,c) )

希望这可以帮助!!

Yield is similar to for loop which has a buffer that we cannot see and for each increment, it keeps adding next item to the buffer. When the for loop finishes running, it would return the collection of all the yielded values. Yield can be used as simple arithmetic operators or even in combination with arrays.
Here are two simple examples for your better understanding

scala>for (i <- 1 to 5) yield i * 3

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)

res: Seq[(Int, Char)] = List((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), (3,a), (3,b), (3,c))

Hope this helps!!

晨曦÷微暖 2024-08-01 19:06:22
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 )

这两段代码也是等价的。

Map 与产量一样灵活,反之亦然。

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 )

These two pieces of code are equivalent.

val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )

println( res3 ) 
println( res4 )

These two pieces of code are also equivalent.

Map is as flexible as yield and vice-versa.

眼藏柔 2024-08-01 19:06:22
val doubledNums = for (n <- nums) yield n * 2
val ucNames = for (name <- names) yield name.capitalize

请注意,这两个 for 表达式都使用 yield 关键字:

for 之后使用 yield 是“秘密武器”,它表示“我想要使用所示的算法,从我在 for 表达式中迭代的现有集合中生成一个新集合。”

取自此处

val doubledNums = for (n <- nums) yield n * 2
val ucNames = for (name <- names) yield name.capitalize

Notice that both of those for-expressions use the yield keyword:

Using yield after for is the “secret sauce” that says, “I want to yield a new collection from the existing collection that I’m iterating over in the for-expression, using the algorithm shown.”

taken from here

鲜肉鲜肉永远不皱 2024-08-01 19:06:22

根据Scala 文档,它明确表示“从现有集合中产生一个新集合”。

另一个 Scala 文档说:“Scala 提供了一种轻量级符号来表达序列推导式。推导式的形式为 for(枚举) yield e,其中 enums 指的是以分号分隔的枚举器列表。枚举器要么是引入新变量的生成器,要么是过滤器。

According to the Scala documentation, it clearly says "yield a new collection from the existing collection".

Another Scala documentation says, "Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enums) yield e, where enums refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter. "

咽泪装欢 2024-08-01 19:06:22

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 )

yield 将打印结果,如:List(5, 6),这很好,

而 map() 将返回结果,如:List(false, false, true, true, true),这可能不是你想要的。

yield is more flexible than map(), see example below

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 will print result like: List(5, 6), which is good

while map() will return result like: List(false, false, true, true, true), which probably is not what you intend.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文