如何表达[42 | x == y]有理解吗?

时间:2013-05-11 19:17:39

标签: scala list-comprehension for-comprehension

我有一些代码片段,我生成多个列表(通过for comprehension),然后连接它们。有一些单元素列表,但这不起作用。在Haskell中,我会做类似

的事情
[42 | i == j]

等同于

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

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

在Scala中我试过

for (if i == j) yield 42

但它说“非法启动简单模式”。

In an answer to what Scala's yield is作者说'Scala'“用于理解”等同于Haskell的“do”符号。

另外,在Scala website上它说“理解的形式为(枚举)产生e ,其中枚举是指以分号分隔的枚举数列表。枚举数是一个引入新变量的生成器,或者它是一个过滤器“。但显然情况并非如此,因为过滤器似乎只允许在生成器之后

目前我使用

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

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

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

1 个答案:

答案 0 :(得分:7)

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

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

Scala language specification个州( 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 ‘<-’ 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中它也是一个对象(但可能也是优化的)。我认为做这么简单的工作效率很低。