2

来自 Scala,CoffeeScript 中缺少阴影似乎很奇怪。我写了一个演示。

object ZekeDemo extends App {

  val filterAll = (arr: Seq[String]) => {
    val saved = ArrayBuffer[String]()
    val removed = ArrayBuffer[String]()

    val filterDo = (arr: Seq[String]) => {
      val saved = for {item <- arr if item != "do"} yield item
      val removed = for {item <- arr if item == "do"} yield item
      (saved, removed)
    }

    val filterSo = (arr: Seq[String]) => {
      val saved = for {item <- arr if item != "so"} yield item
      val removed = for {item <- arr if item == "so"} yield item
      (saved, removed)
    }

    val addRemoved = (item: String) => removedBuff += item
    val addSaved = (item: String) => savedBuff += item

    for {item <- filterDo(arr)._1} { addSaved(item)}
    for {item <- filterDo(arr)._2} { addRemoved(item)}

    for {item <- filterSo(arr)._1} { addSaved(item)}
    for {item <- filterSo(arr)._2} { addRemoved(item)}

    (saved, removed)
  }

  val song = Seq("do", "re", "mi", "fa", "so")

  val s = filterAll(song)._1
  val r = filterAll(song)._2

  println("saved: %s, removed: %s".format(s.mkString(","), r.mkString(",")))

}

现在这是 CoffeeScript 中的相同程序:

filterAll = (arr) -> 

  saved = []
  removed = []

  filterDo = (arr) ->
    saved = ->
      item for item in arr when item != "do"
    removed = ->
      item for item in arr when item == "do"
    {"saved":saved(), "removed":removed()}

  filterSo = (arr) ->
    saved = ->
      item for item in arr when item != "so"
    removed = ->
      item for item in arr when item == "so"
    {"saved":saved(), "removed":removed()}

  addRemoved = (item) ->
    saved[saved.length] = item

  addSaved = (item) ->
    removed[removed.length] = item

  addRemoved item for item in filterDo(arr)["removed"]
  addSaved item for item in filterDo(arr)["saved"]

  addRemoved item for item in filterSo(arr)["removed"]
  addSaved item for item in filterSo(arr)["saved"]

  {"saved":saved, "removed":removed}

song = ["do", "re", "mi", "fa", "so"]

s = filterAll(song)["saved"]
r = filterAll(song)["removed"]

alert("saved: " + s + ", removed: " + r)

声明为数组的“已保存”被引用 for 理解的“已保存”覆盖。更改变量名称会产生预期的输出。

这对我来说似乎很奇怪。函数式语言之美的很大一部分是不需要了解外部作用域。如果我在另一个上下文(类|函数|文件)中编写我的“filterDo”函数,我相信我应该能够将它放到它是有效语法的任何其他上下文中,而不必担心它是否会踩到来自的值一个外部范围。

要求开发人员知道当前范围之外的范围内的所有变量名称的语言会阻止其开发人员进行封装。我可以通过将 filterDo 和 filterSo 移到外部范围来修复这个错误。但这会污染该作用域的命名空间,并不必要地增加界面的表面积。

CoffeeScript 没有为隐藏变量提供特殊语法的论点是,您根本不应该做这种事情。清楚地命名你的变量。因为即使允许使用阴影,如果有两个具有两种不同含义的变量具有相同的名称,一个在内部范围内,一个在封闭范围内,这将是非常令人困惑的。

我原则上喜欢这个想法,但在实践中,我不相信语言应该表现出如此难以根据如此微妙的规则追踪和解决的行为。变量命名是编码风格的反映,风格选择不应该改变程序的行为。

4

1 回答 1

1

我认为在实践中,在编写小型 def 和类等并避免过多/过度通用的全局变量(即,当您编写“好代码”时)时,您不太可能遇到这种情况。你的例子似乎特别做作。这是您的filterAll()功能的更简单的解决方案:

filterAll = (arr) ->
  saved: item for item in arr when item not in ['do', 'so']
  removed: item for item in arr when item in ['do', 'so']

我认为期望编码人员记住给定范围内的变量名称是合理的——如果您根据现代设计标准编写小型定义、类等,这是非常合理的。即使在您设计的示例中,一旦您了解了语言的工作原理,就很容易看到阴影的发生。

此外,默认情况下——除非你明确说明——你编译的 CoffeeScript 文件将自动包装在闭包中。因此,即使您编写“糟糕的代码”并在顶层乱扔一堆通用 var 名称,您也永远不会意外踩到其他文件的“全局”变量(考虑到闭包,不再是全局的;对文件来说是全局的)。

我认为在 Interwebz 上某个:=操作员的建议明确表示分配给同名外部范围内的 var 是一个合理的建议(默认情况下编译器总是会var在范围内重新声明)。然而,此时这将是 CoffeeScript 的一个新的主要版本,因为它会破坏由熟悉该语言的人编写的大量代码,并且实际上会利用您认为的问题。;)

希望这可以帮助。演示 Scala 应用程序以我的名字命名,这是一个意外的喜悦。我喜欢 Scala 和 CoffeeScript!

于 2013-07-26T21:22:04.030 回答