什么是Scala的收益?

时间:2009-06-27 09:18:56

标签: scala functional-programming yield

我了解Ruby和Python的收益率。 Scala的收益率是多少?

9 个答案:

答案 0 :(得分:803)

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

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

Scala的for理解是使用地图flatMapfilter进行多项操作的语法糖。或foreach。 Scala实际上将for - 表达式转换为对这些方法的调用,因此提供它们的任何类或其子集都可以用于理解。

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

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

    被翻译成

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

    被翻译成

    c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
    
  3. 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. for(x <- c; y = ...) yield {...}
    

    被翻译成

    c.map(x => (x, ...)).map((x,y) => {...})
    
  5. 当您查看非常简单的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 (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已从非严格更改为严格。

答案 1 :(得分:201)

它在sequence comprehensions中使用(就像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有不同的效果。

答案 2 :(得分:23)

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

from ... select ??? 
你在Scala中的

for ... yield ???

同样重要的是要理解for - 理解不仅适用于序列,而且适用于定义某些方法的任何类型,就像LINQ一样:

  • 如果您的类型仅定义map,则它允许for - 由a组成的表达式 单一发电机。
  • 如果定义flatMap以及map,则允许for - 包含 几台发电机。
  • 如果它定义foreach,它允许for - 没有收益的循环(包括单个和多个生成器)。
  • 如果定义filter,则允许for - 以if开头的过滤表达式 在for表达式中。

答案 3 :(得分:13)

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

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

类似的东西:

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

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

这与其他语言中yield的“命令式延续”完全不同,它提供了一种生成任意长度列表的方法,来自几乎任何结构的命令式代码。

(如果您熟悉C#,它距离LINQ's select运算符更接近于yield return}。

答案 4 :(得分:12)

Scala中的关键字yield只是语法糖,可以很容易地被map替换为Daniel Sobral already explained

另一方面,如果您正在寻找类似于those in Python的生成器(或延续), yield绝对会产生误导。有关详细信息,请参阅此SO主题:What is the preferred way to implement 'yield' in Scala?

答案 5 :(得分:9)

考虑以下for-comprehension

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

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

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

就数学set-builder notation而言,上述理解类似于

set-notation

可以读作

对于每个整数i如果大于3,则是成员< / strong>集合A。“

或者作为

A是所有整数i的集合,因此每个i都大于3。”

答案 6 :(得分:1)

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

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

res:scala.collection.immutable.IndexedSeq [Int] = Vector(3,6,9,12,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))

希望这会有所帮助!

答案 7 :(得分:0)

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 )

这两段代码也是等效的。

地图与收益一样灵活,反之亦然。

答案 8 :(得分:-3)

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