我知道:
在命令式语言(如C)中,两者之间存在差异,其中运算符与函数不同。但是,在Haskell这样的函数式语言中,两者之间是否存在差异?运算符也是函数?
两者之间的唯一区别是功能内联是否可以在程序的选择性部分上执行,而对整个程序进行部分评估(即∃
vs ∀
)?
两种优化技术之间的语义差异是什么?
答案 0 :(得分:6)
之间存在差异
print(2*2)
编译为print(4)
。这似乎并不局限于运算符表达式,因为您似乎暗示(例如print(sqrt(2.0)
)print(myfunc(2))
可以转换为print(c)
,其中c
是调用myfunc(2)
的结果。然后可以(在" 专业化时间")调用myfunc(2)
来确定c
。当然,如果myfunc
有副作用,例如擦除自己的硬盘而不是程序用户,则会出现严重错误。因此编译器需要某种注释或属性来知道何时允许/期望(例如C ++ 11' s constexpr)内联是一个不相关的概念。内联函数调用意味着用被调用函数的主体替换调用。这个机构没有评估。
在命令式语言(如C)中,两者之间存在差异,其中运算符与函数不同。但是,在Haskell这样的函数式语言中,两者之间是否存在差异?运算符也是函数?
这种独特性(运算符与函数)纯粹是语法,与内联和部分评估之间的差异无关:
函数调用和带运算符的表达式都可以用C进行内联和编译时评估。编译时评估仅限于一组固定的运算符和函数(主要是运算符,但这是历史事故)的表达式
这两个概念都有意义,在Haskell中是不同的。
ghc
有{-# INLINE f #-}
,其中f
无法递归,原因很明显,map f (map g xs
)转换为map (f . g) xs
)。它也可以(但不必)进行内联。 Template Haskell是另一种在编译时(明确地)评估程序的一部分的方法。因此,您的标题问题的答案是:内联评估和部分评估之间的区别与函数和运算符之间的差异无关,在函数式语言中与C语言的区别大致相同。由于副作用(参见上面的擦除硬盘),C中的评估可能更难以实现
答案 1 :(得分:0)
考虑以下代码:
map f xs = case xs of [] -> []; (x:xs') -> f x : map f xs'
f x y = if x % 2 == 0 then y + x / 2 else y + x
main = map (f 3) [1..100]
部分评估f 3
后:
f3 y = if 3 % 2 == 0 then y + 3 / 2 else y + 3
main = map f3 [1..100]
然后在不断折叠3 % 2 == 0
之后:
f3 y = y + 3
main = map f3 [1..100]
现在让我们考虑内联f
:
注意:f
中的f 3
不会被内联,因为它不是一个完整的应用程序,但是我们可以调整f
的定义以使其成为现实,有关更多详细信息,请参见the ghc doc here
f x = \y -> if x % 2 == 0 then y + x / 2 else y + x
main = map (\y -> if 3 % 2 == 0 then y + 3 / 2 else y + 3) [1..100]
然后在不断折叠3 % 2 == 0
之后:
main = map (\y -> y + 3) [1..100]
上面的示例表明,内联评估和部分评估都可以提供额外的优化机会,但是它们仍然有很大的不同:
f 3
)出现多次时,内联将产生很多重复的代码。对于第二点,请考虑以下示例:
main = [f x y | x <- [1..10], y <- [1..100]]
在这种情况下,部分评估可以为f x
动态生成10个专用版本,但是内联却无能为力。