得益于Erik Meijer的讲座,取得了重大进展。好看,也许是暗示。 Haskell允许使用多种方法编写相同的函数。在效率和可读性方面哪一个最好?
sqr' = \x -> x * x
sqr'' x = x * x
sqr''' = (^2)
答案 0 :(得分:3)
在这两个顶级定义之间:
sqr' = \x -> x * x
sqr'' x = x * x
第二个在Haskell程序中几乎是普遍受欢迎的。搜索几乎所有现实世界的Haskell代码,你会发现第二个但很少有的第一个例子。相反,“lambda抽象”(即\x -> ...
语法)最常用于定义匿名函数作为参数传递给更高阶函数。
第二种语法首选有几个原因。首先,它从字面上看更加简洁,从可读性的角度来看,包含较少的不同语法元素(即并置和=
运算符,而不是并置,=
,\
和{{ 1}})。它也很好地概括了使用多种模式定义函数的常见Haskell习语:
->
要使用lambda语法执行此操作,您需要添加一个显式factorial 0 = 1
factorial n | n > 0 = n * factorial (n-1)
构造,其中包含另一个语法元素集。
间:
case
或 - 可能是更公平的比较 - 介于:
之间sqr'' x = x * x
sqr''' = (^2)
这更多是个人偏好的问题。许多Haskell程序员喜欢所谓的point-free语法的简洁外观,其中较大的函数是使用高阶函数和/或组合函数的链组成的,没有显式参数,例如:
sqr'''' x = x^2
sqr''' = (^2)
和mostFrequentWord
= head . maximumBy (comparing length) . group . sort . words
之类的定义更符合这种整体风格。
就这些形式之间含义的差异而言,它实际上有点复杂。由于不明原因与“单态限制”和“违约规则”有关,如果你采用以下模块:
sqr'''
并使用module Square where
sqr' = \x -> x * x
sqr'' x = x * x
sqr''' = (^2)
对其进行编译,ghc -O
和sqr'
的定义将是等效的 - 两者都专门用于sqr'''
类型的操作,并且会准确生成相同的代码。 (经GHC 8.0.2测试)。相比之下,Integer
仍然具有签名sqr''
的多态性,这意味着它可以在任何数字类型上运行。
如果您添加顶级类型签名(无论如何都是好的做法!),如下所示:
Num a => a -> a
然后它们都生成完全相同的代码。您可以通过查看生成的“核心”(编译器在编译过程中创建的中间Haskell类语言)来自行验证:
module Square where
sqr', sqr'', sqr''' :: (Num a) => a -> a
sqr' = \x -> x * x
sqr'' x = x * x
sqr''' = (^2)
在生成的核心中,您将看到定义:
ghc -O -ddump-simpl -dsuppress-all -fforce-recomp Square.hs
看起来很奇怪,但基本上说,将适当的sqr' = \ @ a_aBC $dNum_aLW x_arx -> * $dNum_aLW x_arx x_arx
类型的*
操作应用于参数Num
。生成的代码有两种变体:
x_arx x_arx
表明GHC认为它们与sqr'' = sqr'
sqr''' = sqr'
之间没有区别,因此不存在语义或性能差异。