【问题标题】:Scala's for-comprehensions: vital feature or syntactic sugar?Scala 的理解:重要特性还是语法糖?
【发布时间】:2023-03-06 00:17:02
【问题描述】:

当我第一次开始研究 Scala 时,我喜欢用于理解的外观。它们看起来有点像我在 Java 5 中习惯使用的 foreach 循环,但有功能限制和很多优美的语法。

但是当我吸收了 Scala 风格时,我发现每次可以使用压缩时,我都会使用 mapflatMapfilterreduceforeach。这样,代码的意图对我来说似乎更清楚,潜在的隐藏惊喜更少,而且它们通常也是更短的代码。

据我所知,for-comprehensions 总是被编译成这些方法,所以我想知道:它们到底是干什么用的?我是否错过了一些功能上的更新(这不是第一次)?理解是否能做其他功能不能做的事情,或者至少会更笨拙?它们是否在特定用例下大放异彩?真的只是个人喜好问题吗?

【问题讨论】:

标签: scala list-comprehension


【解决方案1】:

请参考this question。简短的回答是,理解可以更具可读性。特别是,如果您有许多嵌套的生成器,您所做的实际范围会变得更加清晰,并且您不需要大的缩进。

【讨论】:

    【解决方案2】:

    for-comprehension 的另一个重要用途是用于内部 DSL。 ScalaQL 就是一个很好的例子。可以转这个

    val underAge = for { 
      p <- Person 
      c <- Company 
      if p.company is c 
      if p.age < 14 
    } yield p 
    

    进入这个

    SELECT p.* FROM people p JOIN companies c ON p.company_id = c.id WHERE p.age < 14 
    

    还有更多。

    【讨论】:

    • @Erik 当我尝试它时它起作用了。它是一个 PDF 文件的链接。如果您仍然无法下载该文件,只需 google 一下:ScalaQL:Daniel Spiewak 和 Tian Zhao 的 Scala 语言集成数据库查询
    • 被否决:实际的问题是,理解是否比脱糖有任何优势 - 您也可以以脱糖形式编写该代码。 ScalaQuery 是一个更好的例子,因为它具有可用的生产质量实现。那篇论文瞥见了太多值得推荐的重要细节。
    【解决方案3】:

    for-comprehensions 是语法糖,但这并不意味着它们不重要。它们通常比扩展形式更简洁,这很好,但也许更重要的是,它们可以帮助命令式语言的程序员使用函数式构造。

    当我第一次开始使用 Scala 时,我经常使用 for-comprehensions,因为它们很熟悉。然后我几乎完全停止了,因为我觉得使用底层方法更明确,因此更清晰。现在我又开始使用 for-comprehensions,因为我认为它们更好地表达了我正在做的事情的意图,而不是做这件事的方式。

    【讨论】:

      【解决方案4】:

      在某些情况下,因为理解可以更好地表达意图,所以当他们这样做时,使用它们。

      还要注意,使用 for 推导式,您可以免费获得 模式匹配。例如,使用 for comprehension 对 Map 进行迭代要简单得多:

      for ((key, value) &lt;- map) println (key + "--&gt;" + value)

      与 foreach 相比:

      map foreach { case (key, value) =&gt; println (key + "--&gt;" + value) }

      【讨论】:

        【解决方案5】:

        你是对的。 for-comprehension 是语法糖。我相信,一旦你习惯了底层的方法,它们就会更简洁、更容易阅读。

        比较以下等效语句:

        1. for (i <- 1 to 100; if (i % 3 == 0)) yield Math.pow(i, 2)
        2. (1 to 100).filter(_ % 3 == 0).map(Math.pow(_, 2))
        

        在我看来,#1 中添加的分号分散了人们认为这是一个单链语句的感觉。还有一种感觉是 i 是一个 var(是 1,还是 99,还是介于两者之间?),这有损于其他功能样式。

        选项 2 显然是对对象的一系列方法调用。链条中的每个环节都清楚地说明了它的责任。没有中间变量。

        也许包含推导式是为了方便从 Java 过渡的开发人员。无论如何,选择哪一个是风格和偏好的问题。

        【讨论】:

        • “一旦你习惯了它们” - 完全正确。一旦我理解了 monad,我就切换到了底层方法。在此之前,for 理解是一个有用的链接。
        • 您在 1 中根本不需要分号:for (i &lt;- 1 to 100 if (i % 3 == 0)) yield Math.pow(i, 2)。此外,您可以用大括号替换括号以激活分号推理。
        • 谢谢@Blaisorblade 我想知道在 2009 年是否需要分号。
        猜你喜欢
        • 2013-07-05
        • 1970-01-01
        • 2020-07-24
        • 1970-01-01
        • 2011-10-26
        • 2013-06-22
        • 1970-01-01
        • 2010-10-31
        • 1970-01-01
        相关资源
        最近更新 更多