我有一些代码片段,我在其中生成多个列表(通过用于理解)然后将它们连接起来。但是,有一些单元素列表不起作用。在 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/