3

我有一些代码在其中生成多个列表(通过理解),然后将它们连接起来。但是,有些单元素列表不起作用。在 Haskell 我会做类似的事情

[42 | i == j]

等效地是

(do guard (i == j)
    return 42) :: [Int]

或者

(guard (i == j) >>= \_ -> return 1) :: [Int]

在斯卡拉我试过

for (if i == j) yield 42

但它说“简单模式的非法开始”。

在回答 Scala 的产量是什么时,作者说“Scala 的“for comprehensions”等同于 Haskell 的“do”notation”。

此外,在Scala 网站上,它说“理解具有(enums) yield e的形式,其中 enums 是指以分号分隔的枚举器列表。枚举器要么是引入新变量的生成器,要么是过滤器”。但显然,情况并非如此,因为过滤器似乎只允许生成器之后使用。

目前我使用

if (i == j) List(42) else Nil

对于这种特殊情况,我可能不喜欢 for 理解语法,而只使用 if-then-else 代替。在 Haskell 中,由于与数学集合构建符号的相似性,它看起来相当不错。

我的问题不是关于风格,而是更多关于技术细节:为什么在 Haskell 和 Scala 之间的这个特定案例中存在差异?为什么不for (if i == j) yield 42工作?

4

1 回答 1

7

最接近的等价物[42 | i == j]可能是for (x <- List(42) if i == j) yield x.

for (if i == j) yield 42是非法的,因为过滤器(if部分)必须跟随某个生成器(x <- List(42)在我的示例中)。

Scala 语言规范状态(6.19 For Comprehensions and For Loops):

句法:

Expr1       ::= ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’)
                  {nl} [‘yield’] Expr
Enumerators ::= Generator {semi Enumerator}
Enumerator  ::= Generator
                | Guard
                | ‘val’ Pattern1 ‘=’ Expr
Generator   ::= Pattern1 ‘&lt;-’ Expr [Guard]
Guard       ::= ‘if’ PostfixExpr

如您所见,. 中至少需要一个生成器Enumerators

编辑

顺便说一句,我认为这if (i == j) List(42) else Nil是正确的做法,因为它不是 Haskell。它很干净而且很可能更快,因为它只构建一次列表并且不调用任何其他方法。

我的示例由编译器翻译成List(42) withFilter (x => i == j) map (x => x)(实际上可能已优化,我不确定)并且可以缩写为List(42) filter (x => i == j). 您可以看到,它构造了初始列表,然后调用了一个创建新列表的方法,并且该方法采用匿名函数,在 Scala 中它也是一个对象(但可能它也被优化了)。我认为做这么简单的工作效率低下。

于 2013-05-11T19:52:23.057 回答