Haskell下划线变量与显式变量的区别

10
我已经学习Haskell几周了,我对下划线(_)作为函数参数的使用有一个问题。我认为通过具体示例来提问会更好。假设我想定义一个函数,根据提供的索引从列表中提取元素 - 是的,我意识到 (!!) 已经预定义了。我可以定义函数的两种方式如下(我相信还有其他方式):

版本1

indexedElement             :: [a] -> Int -> a
indexedElement xs n | n < 0 = error "Index can not be negative."
indexedElement [] n         = error "Index must be smaller than the length of the list."
indexedElement (x:xs) 0     = x
indexedElement (x:xs) n     = indexedElement xs (n - 1)

版本 2

indexedElement            :: [a] -> Int -> a
indexedElement _ n | n < 0 = error "Index can not be negative."
indexedElement [] _        = error "Index must be smaller than the length of the list."
indexedElement (x:_) 0     = x
indexedElement (_:xs) n    = indexedElement xs (n - 1)

两个版本明显非常相似。两者之间的唯一区别在于使用显式变量或下划线。对我来说,_表示可以写任何东西,而像n这样的显式变量则更明显地表明参数必须是整数。因此,我更喜欢版本1;但是,(!!)的GHC源代码却像版本2那样编写。第二个版本有功能上的优势吗?如果没有,那么“纯粹”的Haskell程序员会反对版本1吗?我理解在特定语言中编程时,保持一致的编写代码方式的重要性,所以我尝试遵循“非书面规则”。这是一个例子,我更喜欢第一个版本,但我认为它并不会使代码更难阅读。我不知道这是否与我在纯数学方面的背景有关,但我想听听你们更有经验的Haskell老手怎么看。

6
我不确定性能差异(您可以尝试输出两个版本的核心以查看是否有差异),但大多数Haskeller会更喜欢第二个版本,因为它减少了需要绑定的名称数量。下划线 _ 表示任何值都可以放在那里(只要它是正确的类型,这由类型注释给出)。虽然在未使用时使用 n 表示整数可能感觉更自然,但请记住,并非所有人都会有这种偏见。 - bheklilr
5
您可以通过将变量命名为“_n”来达成折中,这样使用“-Wall”时就不会收到警告,同时也表明该变量未被使用,并给出了变量的名称。请注意,这并不改变原意。 - luqui
1
@basketballfan22,luqui的方法最近也变得惯用了,我非常喜欢它。 - dfeuer
1
@Sibi,我确实经常看到它,所以肯定有一些人在使用,很可能包括“base”,但我无法立即指出任何特定的事情。 - dfeuer
2
@bheklilr,肯定不会有任何性能差异。Alpha转换只会影响那些具有过多反射或疯狂编译器的语言中的任何内容。 - dfeuer
显示剩余4条评论
1个回答

23

第二个版本有功能上的优势吗?

我认为它们没有任何操作上的差异。但我觉得第二个版本更易读。下划线_表示它根本没有被使用。因此在阅读代码时,我可以忽略它,只集中注意其他参数。而在第一个版本中,我会想n是否已定义,但作者可能忘了使用它?或者也许该参数并非必需。第二个版本避免了这种心理负担。但这只是我的个人意见。:)

实际上,如果您启用警告标志(-Wall)并编译代码,它会对您的第一个版本发出警告:

[1 of 1] Compiling Main             ( code.hs, code.o )

code.hs:2:16: Warning: Defined but not used: ‘xs’

code.hs:3:19: Warning: Defined but not used: ‘n’

code.hs:4:19: Warning: Defined but not used: ‘xs’

code.hs:5:17: Warning: Defined but not used: ‘x’

code.hs:8:17: Warning: Defined but not used: ‘xs’

非常感谢。正如我在帖子中提到的,我还没有接触Haskell编程很长时间;因此,我甚至不知道-Wall的存在。如何打开它?我只需要在源代码的开头输入-Wall吗?如果是这样,它应该在任何导入之前吗? - philomathic_life
1
@basketballfan22:这是编译器的命令行选项。如果你使用ghc命令手动编译,你需要在那里提供它。如果你使用像Cabal或Stack这样的构建工具,它们的文档将说明如何配置项目的GHC选项。 - Luis Casillas
@LuisCasillas:啊!太棒了。感谢您的帮助。 - philomathic_life
1
@basketballfan22,另一个选项是在你的.hs文件中的module行上方的某个位置放置{-# OPTIONS_GHC -Wall #-} - dfeuer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接