共享意味着如果要使用多次,则将存储临时数据。也就是说,一个函数只对它的参数求值一次。一个例子是:
let x = sin x in x * x
还有哪些其他功能有助于共享,它们将如何与执行IO的实际程序的需求互动?
最佳答案
函数式编程中最清晰的共享示例来自Clean,它基于图形重写。在那里,计算涉及到DAG,因此我们可以将表达式(sin x) * (sin x)
视为
(*)
/ \
sin x sin x
图重写系统具有明确的共享概念,因此我们可以将该计算表示为
(*)
/ \
\ /
sin x
将乘法节点指向同一节点,从而共享
sin x
的计算。术语重写系统没有如此明确的共享概念,但是优化仍然很重要。在GHC中,有时可以表示与局部变量的共享,例如改写f x = (sin x) * (sin x)
进入
f x = sinx * sinx
where sinx = sin x
但是由于两者在语义上是等效的,因此编译器可以自由地以相同的方式实现这两种方式,无论共享与否。根据我的理解,GHC通常会保留使用局部变量引入的共享,有时还会引入共享(将共享添加到第一个变量),但是在术语重写系统中没有共享的正式表达,要么行为取决于实现(请参阅tel的评论和回答)。
共享涉及IO,因为无法共享副作用值。如果我们认为一种不纯正的语言,
(string-append (read-line)
(read-line))
和
(let ((s (read-line)))
(string-append s s))
第一个执行两次IO操作,向用户请求两行并附加它们;第二行执行IO操作。第二个“共享” IO操作,执行一次并将其附加到自身。通常,共享一个纯计算会减少执行时间而不会更改程序的结果,而共享一个副作用值(一个可能随时间变化或与用户交互的值)会更改结果。为了使编译器自动共享计算,它需要知道它们是纯净的,因此减少求值次数无关紧要。 Clean通过唯一性类型来做到这一点; IO操作具有类型属性UNQ,该属性告诉编译器不应共享该属性。 Haskell对IO monad的处理方式有所不同。
关于haskell - 在功能编程语言的实现中共享指的是什么,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16430376/