如何避免两次进行函数调用?

Con*_*sed 1 haskell

Haskell新手问题:是否有一种技术可以提高效率并避免对tail原始列表进行两次调用

func1 originalList =
    func2 (doSomething (tail originalList)) (doSomethingDifferent (tail originalList))

doSomething listA =
....

doSomethingDifferent listB =
....

func2 listA listB =
....
Run Code Online (Sandbox Code Playgroud)

Wil*_*sem 9

命令式编程语言中,您只需调用一次函数,并将其存储到变量中.

在像Haskell这样的函数式编程语言中,您使用whereor let子句,因此也使用变量:

func1 originalList = func2 (doSomething t) (doSomethingDifferent t)
    where t = tail originalList
Run Code Online (Sandbox Code Playgroud)

所以在这里,你将构建一个表达,和论据doSomething,并doSomethingDifferent参考到同一个表达式树.现在,因为哈斯克尔懒惰地工作 tail originalList不会立即进行评估.

但是如果它(部分)被评估(例如在评估func2调用的第一个操作数时),则在通过另一种方式(例如第二个参数)计算表达式树时,不会重做该工作.现在,无论如何tail都没有太多工作.但如果它是一个更复杂的功能,它会带来更多回报.

请注意 - 就像@amalloy所说 - 如果是tail函数,则不必使用where子句:简单模式匹配就足够了:

func1 (_:t) = func2 (doSomething t) (doSomethingDifferent t)
Run Code Online (Sandbox Code Playgroud)

请注意,这两者并不完全等效:因为现在存在一个约束,即只有列表包含至少一个元素时才会触发该行.有可能func2只是忽略两个参数,这样就不需要获得尾部(尽管这里不太可能).