scala的yield是多少?

我理解Ruby和Python的优点。Scala的yield是做什么的?

141308 次浏览

它在顺序理解中使用(就像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)
}
}

f#中对应的表达式为

[ for a in args -> a.toUpperCase ]

from a in args select a.toUpperCase

在Linq中。

Ruby的yield具有不同的效果。

除非你能从Scala用户那里得到更好的答案(我不是),以下是我的理解。

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

喜欢的东西:

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

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

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

(如果你熟悉c#,它更接近LINQ的 select操作符,而不是yield return)。

是的,正如Earwicker所说,它几乎等同于LINQ的select,与Ruby和Python的yield几乎没有关系。基本上,在c#中你会写

from ... select ???

而在Scala中则相反

for ... yield ???

同样重要的是要理解__abc0 -推导式不仅适用于序列,还适用于任何定义特定方法的类型,就像LINQ:

    如果你的类型只定义了map,它允许包含a的__abc1 -表达式 李单发电机。< / >
  • 如果它定义了flatMapmap,它允许__abc2 -表达式组成
  • 如果定义了foreach,则允许无yield的__abc1 -循环(包括单个生成器和多个生成器)。
  • 如果它定义了filter,它允许以if开头的for-filter表达式 在for表达式中

我认为公认的答案很好,但似乎许多人没有抓住一些基本的问题。

首先,Scala的for推导式等同于Haskell的do表示法,它只不过是组合多个一元操作的语法糖。因为这句话很可能对任何需要帮助的人都没有帮助,让我们再试一次…:-)

Scala的for推导式是map、flatMapfilter组合多个操作的语法糖。或foreach。Scala实际上将__abc0 -表达式转换为对这些方法的调用,因此任何提供它们的类或它们的子集都可以用于推导式。

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

  1. < p >这

    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) yield {...}
    

    被翻译成

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

    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方法可用,则回退到前者。

    .

    .
  4. < p >这

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

    被翻译成

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

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 (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的行为。作为旁注,Range在Scala 2.7和Scala 2.8之间从非严格变成了严格。

关键字yield在Scala中只是一个语法糖可以很容易地用map替换,如详细的Daniel Sobral已经解释过了

另一方面,yield绝对是误导如果你正在寻找类似于Python中的那些的生成器(或延续)。有关更多信息,请参阅此SO线程:实现'yield'在Scala中?

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),这可能不是你想要的。

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和yield一样灵活,反之亦然。

考虑下面的对于理解

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

大声读出来可能会有帮助

"对于每个整数i如果大于3,则yield (produce) i并将其添加到列表A。"

在数学set-builder符号方面,上面的for-comprehension类似于

set-notation

这可以理解为

对于每个整数i如果它大于3,那么它集合的成员 a 。"

或者作为替代

A是所有整数i的集合,使得每个i大于3。"

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)] =列表((1),(b)、(1 c), (2), (2 b), (2 c), (3), (b)、(3 c))

希望这能有所帮助!!

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

注意,这两个for表达式都使用收益率关键字:

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

取自在这里