功能类型A - > B在某种意义上不是很好。虽然功能是一等值,但由于效率问题,通常无法自由操作它们。你不能应用太多的转换(A - > B) - > (C - > D),在某些时候你必须计算一个值。
显然这是由于 - >的非严格性质。
有很多熟悉的技巧来处理Double类型的函数 - >双。可以将它们表示为给定某个基础的向量,其可以包括trig函数,多项式等。
是否有任何一般技巧可以解决A的无效率问题? B型?
或替代 - > ?
答案 0 :(得分:5)
您的关注似乎是h === f • g
,f • g
通常效率低于h
。给定编译时已知的函数组合,编译器执行两个技巧,可以使f • g
比您怀疑的更有效 - 内联和融合。内联避免了第二个函数调用的额外间接,并为优化开辟了许多新的机会。流融合(或构建/折叠融合)可以(给出基本示例)将诸如map f . map g
的组合转变为map (f . g)
,从而通过常数因子减少结构的遍历次数。 Fusion不仅在列表上运行,还在其他结构上运行,并提供了Haskell库(如Vector
)高效性能的一个原因。
捷径融合:http://www.haskell.org/haskellwiki/Correctness_of_short_cut_fusion
答案 1 :(得分:3)
我无法证实这一点。作为AFRP的高效用户和实现者,我正在对完全多态函数进行大量转换,深度嵌套以及长时间运行的应用程序。请注意,Haskell编译器不使用传统的基于堆栈的函数调用范例。他们使用图缩减算法。我们没有与C相同的问题。
答案 2 :(得分:2)
最常见的技巧之一是memoization - 在计算函数后存储函数的值。链接:Haskellwiki,SO example,MemoCombinators。正如你所提到的,另一个技巧是你有一个很好的函数类型(多项式,向量,泰勒级数等) - 然后它可以表示为列表,表达式等。
答案 3 :(得分:1)
FWIW:在Felix中,整个程序分析器严重依赖内联性能,函数参数有三种:渴望,懒惰或“让编译器决定”。
在评估函数体之前,评估Eager参数并将其赋值给变量。
通过将参数替换为参数表达式来计算延迟参数。
默认为“让编译器决定”。对于大量的“普通”代码(无论这意味着什么),无论您是使用急切还是懒惰的评估,它都没有任何区别。
通常在Felix懒惰评估中更快:请注意这并不意味着关闭。这意味着内联。但是,有时编译器会选择急切的评估,它会减少代码膨胀,过多的内联会产生反作用。我没有声称算法有任何好处..但是Felix有时可以胜过C和Ocaml(GHC没有进入决赛)。
作为一个简单的例子..类型类。 Felix有类型类,有点像Haskell。没有或只有很少的性能开销..当然没有词典!
在我看来,如果你只是抛弃了单独编译的古老概念,那么Haskell会好得多:整个程序分析器可以做得更多,文本比目标代码更快地工作(给定完全自由)缓存编译结果)。让懒惰的语言使用为急切评估而设计的编译模型真是太疯狂了!
Haskell变体可能尝试的另一件事是放弃所有函数都是懒惰的想法,而是采用评估策略无关的想法,除非另有说明。这可能会带来更多优化机会。