函数式编程中过程和数据的区别?

Difference between procedure and data in functional programming?

我是函数式编程的新手。我正在读 SICP 本书。我对用于过程和数据的术语感到困惑。

程序和数据有什么区别?他们真的一样吗?

如有任何帮助,我们将不胜感激。谢谢。

复合过程是由一个或多个其他过程组成的过程。这是一个简单、直接的定义,但当我们尝试将相同的推理应用于数据时,它会变得有点诡异。

我相信 Harold Abelson 在讲座中给出的例子大致如下:

这是凭记忆转述 – Harold Abelson,我希望我在这里对你公平。

Imagine we were to make a +rat procedure that took 4 numbers: numer1, numer2, denom1, and denom2. Calling it might look like:

;; naively add two rational numbers
(+rat numer1 denom1 numer2 denom2)

Now imagine if we wanted to expand this further to support multiplication, you would continue to have to introduce more variables for each rational number

;; add then multiply
(define rat1 (+rat numer1 denom1 numer2 denom2))
(*rat (numer rat1) (denom rat2) numer3 denom3)

This is getting really messy really quick. What if we could treat our rational number data like we do simple number data? What if we didn't have to always consider the individual components (numer and denom)? What if we could deal with our rational numbers as compound data ?

;; add two rational numbers
(define m (make-rat 3 4))
(define n (make-rat 5 6))
(+rat m n)

;; add then multiply
(define o (make-rat 1 2))
(*rat (+rat m n) o)

Now we can express our computations in a much better way. Compare this to adding whole numbers

(+ 1 2)
(* (+ 1 2) 3)

We can reason about these expressions very easily. There's no need for us to be concerned with the individual components (numer and denom) of our rational numbers, we can work directly with the compound data. This lifts an incredible burden on us as our program continues to expand in complexity.

;; naive procedure
(define (+rat n1 d1 n2 d2)
  (make-rat (+ (* n1 d2)
               (* n2 d1))
            (* d1 d2)))

;; compound data procedure
(define (+rat x y)
  (make-rat (+ (* (numer x) (denom y))
               (* (numer y) (denom x)))
            (* (denom x) (denom y))))

In the non-naive implementation, the procedure expects x and y to be values that were constructed with make-rat – or at the very least, they must have valid numer and denom selectors. Writing the procedure this way means we can pass compound data around – instead of simple, separate pieces – and pick it apart inside our procedures as necessary.

paraphrasing from memory; source: SICP Lecture 2B: Compound Data

无论如何,你问这个问题是对的。复合过程和复合数据具有相似的语法这一事实意味着我们有效地模糊了代码和数据之间的界限

脑洞大开:代码就是数据;数据是代码。

继续阅读 SICP。它改变了我的生活。


红利汽水!

这种数据即代码的想法对我来说非常有趣。我学习其他语言的时候从来没有质疑过对象和数组之类的东西。但是当我学习 SICP 时,以不同的方式思考数据,我质疑 conscarcdr它们到底是什么?

最终,它们如何实施并不重要。如果我们只关心这 3 个过程,我们必须考虑它们履行的合同......

- - - - Contract - - - -

For any x and y

  (car (cons x y)) is x

  (cdr (cons x y)) is y

- - - - - - - - - - - - -

直到我研究了 Lambda Calculus我才看到了我见过的最美丽的事物之一。我将在 Scheme 中向您展示……

(define (cons x y)
  (lambda (p) (p x y)))
  
(define (car p)
  (p (lambda (x y) x)))
  
(define (cdr p)
  (p (lambda (x y) y)))

(println (car (cons 'x 'y))) ;; => 'x
(println (cdr (cons 'x 'y))) ;; => 'y

什么! “一对”在哪里? “清单”在哪里?数据的容器在哪里?数据存储在哪里?它就在代码中。

代码为数据;数据是代码。

当然这可能不是 conscarcdr 的实际实施方式,但我们已经履行了合同,我们可以开始构建各种非常酷的东西这个。我们只用 lambda 就创建了一个数据结构——凭空而来!