关于函数式编程:Scala的收益是多少?

What is Scala's yield?

我了解Ruby和Python的产量。斯卡拉的产量是多少?


我认为公认的答案是伟大的,但似乎许多人没有抓住一些基本点。

首先,scala的for理解相当于haskell的do表示法,它只不过是组成多个单元运算的语法糖。因为这句话很可能不会帮助任何需要帮助的人,我们再试一次…:-)

scala对for的理解是用map、flatMapfilter组成多个操作的语法糖。或foreach。scala实际上将for表达式转换为对这些方法的调用,因此提供这些方法的任何类或其中的一个子集都可以用于理解。

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

  • 这个

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

    被翻译成

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

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

    被翻译成

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

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

    在scala 2.7中转换为

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

    或者,在scala 2.8中,

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

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

  • 这个

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

    被翻译成

    1
    c.map(x => (x, ...)).map((x,y) => {...})
  • 当你看到非常简单的for理解时,mapforeach替代方案看起来确实更好。但是,一旦开始编写它们,就很容易在括号和嵌套级别中丢失。当这种情况发生时,for的理解通常更清楚。

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

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

    1
    2
    3
    4
    5
    for {
      sl <- l
      el <- sl
      if el > 0
    } yield el.toString.length

    withFilter

    scala 2.8引入了一个名为withFilter的方法,其主要区别在于,它不返回新的、经过过滤的集合,而是按需过滤。filter方法的行为是根据集合的严格性定义的。为了更好地理解这一点,让我们来看一些使用EDOCX1(严格)和EDOCX1(非严格)的scala 2.7:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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影响它。为了说明这一点,以下是理解代码的等价物:

    1
    2
    3
    4
    5
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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从非严格改为严格。


    它被用于序列理解(例如Python的列表理解和发生器,你可以使用yield)。

    它应用于与for的组合,并将一个新的元素写入结果序列。

    简单的例子(选自scala-lang)

    1
    2
    3
    4
    5
    6
    7
    /** 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)
      }
    }

    对应表达式

    ZZU1

    黄金

    1
    from a in args select a.toUpperCase

    成人关系

    Ruby's yieldhas a different effect.


    是的,正如厄尔维克所说,这是一个很好的等值链接,它的select和鲁比和Python的yield。基本上,你会写在哪里

    1
    from ... select ???

    在你之前

    1
    for ... yield ???

    了解for的另一个重要方面是,理解不只是一个序列,而是一个确定某些方法的类型,就像Linq:

    • 如果你的类型的定义是单一发生器
    • 如果它的定义与map一样,它允许for-表达式由几种发电机
    • 如果它的定义是foreach,它允许for无产量的LOOOPS(两者兼有单个和多个发生器)。
    • 如果它的定义是filter,它允许for过滤器表达式以if开始在for


    除非你有一个更好的答案从一个大型用户(我不知道),这就是我的理解。

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

    像这样的事

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

    因此,每个输入都有一个输出项目(尽管我相信有一个吸收复制品的方法)。

    这与其他语言中的"imperative continuctions"不同,因为它提供了一种方法来产生一个长度的列表,从某种imperative code with almost any structure。

    (如果你熟悉C 35;)


    scala中的关键字yield只是简单的句法糖,可以很容易地用map替换,正如DanielSobral已经详细解释过的那样。

    另一方面,如果您在寻找类似于Python中的生成器(或延续),那么yield绝对是一种误导。有关更多信息,请参阅这个so线程:在scala中实现"yield"的首选方法是什么?


    考虑以下内容以便理解

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

    按如下方式大声读出可能会有所帮助

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

    就数学集合生成器符号而言,上述理解类似于

    set-notation

    可以理解为

    对于每个整数i,如果它大于3,则它是集合A的成员。

    或者作为

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


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

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

    res:scala.collection.immutable.indexedseq[int]=矢量(3、6、9、12、15)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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,a),(1,b),(1,c),(2,a),(2,b),(2,c),(3,a),(3,b),(3,c)

    希望这有帮助!你看!


    1
    2
    3
    4
    5
    6
    7
    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 )

    这两段代码是等效的。

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

    println( res3 )
    println( res4 )

    这两段代码也是等效的。

    地图和产量一样灵活,反之亦然。


    yield比map()更灵活,请参见下面的示例

    1
    2
    3
    4
    5
    6
    7
    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),这可能不是您想要的结果。