我有一些代码片段,我生成多个列表(通过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
没有工作?
答案 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中它也是一个对象(但可能也是优化的)。我认为做这么简单的工作效率很低。