如果我使用lambda表达式定义函数或者在使用GHC编译模块时没有这样做会有什么不同呢
f :: A -> B
f = \x -> ...
VS
f :: A -> B
f x = ...
我认为我看到它有助于编译器内联函数,但除此之外,如果我从第一个版本更改为第二个版本,它会对我的代码产生影响。
我正在尝试理解其他人的代码,并在第一种方式而非第二种方式中推断出为什么要定义此功能的原因。
答案 0 :(得分:8)
为了回答这个问题,我用两种方式编写了一个小程序,并查看了Core生成的内容:
f1 :: Int -> Int
f1 = \x -> x + 2
{-# NOINLINE f1 #-}
f2 :: Int -> Int
f2 x = x + 2
{-# NOINLINE f2 #-}
我通过运行ghc test.hs -ddump-simpl
获得核心。相关部分是:
f1_rjG :: Int -> Int
[GblId, Arity=1, Str=DmdType]
f1_rjG =
\ (x_alH :: Int) -> + @ Int GHC.Num.$fNumInt x_alH (GHC.Types.I# 2)
f2_rlx :: Int -> Int
[GblId, Arity=1, Str=DmdType]
f2_rlx =
\ (x_amG :: Int) -> + @ Int GHC.Num.$fNumInt x_amG (GHC.Types.I# 2)
结果是相同的,所以回答你的问题:从一种形式转换到另一种形式没有影响。
话虽如此,我建议查看左下方的答案,该答案涉及实际存在差异的情况。
答案 1 :(得分:5)
首先,第二种形式更灵活(它允许你进行模式匹配,下面的其他条款用于替代案例)。
当只有一个子句时,它实际上等同于一个lambda ...除非你有一个where
范围。即,
f = \x -> someCalculation x y
where y = expensiveConstCalculation
比
更有效f x = someCalculation x y
where y = expensiveConstCalculation
因为在后者中,当您使用不同的参数评估y
时,始终会重新计算f
。在lambda表单中,y
被重用:
f
的签名是单态的,则f
是constant applicative form,即全局常量。这意味着y
在整个程序中共享,someCalculation
的每次调用只需要f
重新执行y
。这通常是理想的性能,当然它也意味着f
不断占用内存。map f longList
是多态的,那么它实际上隐含地是您使用它的类型的函数。这意味着你不能获得全球共享,但是如果你写的是y
,然后在映射到列表之前,只需要计算一次Sub PT_cache_clear()
For Each pc In ActiveWorkbook.PivotCaches
pc.Clear
Next pc
End Sub
。这是性能差异的要点。现在,当然GHC可以重新排列内容,因为它保证结果是相同的,如果认为效率更高,它可能总是将一种形式转换为另一种形式。但通常情况并非如此。