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

[42 | i == j]

这相当于
(do guard (i == j)
    return 42) :: [Int]

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

在斯卡拉我试过
for (if i == j) yield 42

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

In an answer to what Scala's yield is 作者说“Scala 的“for comprehensions”相当于 Haskell 的“do”符号。

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

目前我使用
if (i == j) List(42) else Nil

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

我的问题不是关于风格,而是更多关于技术细节:为什么 Haskell 和 Scala 在这个特定情况下有区别?为什么 for (if i == j) yield 42 不起作用?

最佳答案

[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 和 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 中它也是一个对象(但它可能也被优化了)。我认为做这么简单的工作效率低下。

关于scala - 如何表达[42 | x == y] 用于理解?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16500819/

10-15 23:45