我刚刚在计算机程序的结构和解释中遇到了一个想法:
数据只是愚蠢的代码,代码只是智能数据
我不明白这意味着什么。有人可以帮助我更好地理解它吗?
这是 SICP 的基本课程之一,也是计算机科学最强大的思想之一。它是这样工作的:
我们认为的“代码”实际上并没有自己做任何事情的能力。代码仅在解释的上下文中定义程序——在该上下文之外,它只是一个字符流。(实际上是比特流,实际上是电脉冲流。但让我们保持简单。)代码的含义是由运行它的系统定义的——这个系统只是把你的代码当作数据来告诉你它是你想做的。C 源代码被 C 编译器解释为描述您希望它创建的目标文件的数据。加载器将目标文件视为描述要排队执行的某些机器指令的数据。
解释型语言通常包含将数据视为代码的机制,这意味着您可以将代码以某种形式传递给函数,然后执行它——甚至在运行时生成代码:
#!/usr/bin/perl
# Note that the above line explicitly defines the interpretive context for the
# rest of this file. Without the context of a Perl interpreter, this script
# doesn't do anything.
sub foo {
my ($expression) = @_;
# $expression is just a string that happens to be valid Perl
print "$expression = " . eval("$expression") . "\n";
}
foo("1 + 1 + 2 + 3 + 5 + 8"); # sum of first six Fibonacci numbers
foo(join(' + ', map { $_ * $_ } (1..10))); # sum of first ten squares
像 scheme 这样的一些语言有一个“一等函数”的概念,这意味着你可以把一个函数当作数据来传递,而不用评估它,直到你真的想要。
结果是“代码”和“数据”之间的划分几乎是任意的,只是透视的功能。抽象级别越低,代码必须越“智能”:它必须包含更多关于如何执行的信息。另一方面,解释器提供的信息越多,代码就越愚蠢,直到它开始看起来像完全没有智能的数据。
编写代码最有效的方法之一是简单地描述您需要什么:数据将转换为代码,描述如何通过解释性上下文为您提供所需的内容。我们称之为“声明式编程”。
举一个具体的例子,考虑 HTML。HTML 没有描述图灵完备的编程语言。它只是结构化数据。它的结构包含一些智能,可以让它控制其解释上下文的行为——但不是很多智能。另一方面,它比普通网页上出现的文本段落包含更多的智能:那些是非常愚蠢的数据。
在安全方面:由于缓冲区溢出,您认为是数据并因此无害的内容(例如图像)可以作为代码执行并 p0wn 您的机器。
在软件开发的背景下:许多开发人员非常害怕“硬编码”的东西,并且非常热衷于提取可能必须更改为配置文件的参数。这通常基于配置文件只是“数据”的想法,因此可以轻松更改(可能由客户),而不会引发更改代码中任何内容的问题(编译、部署、测试)。
这些开发人员没有意识到,既然这些“数据”会影响程序的行为,那么它确实是代码;它可能会破坏程序,并且在这种更改之后不需要进行完整测试的唯一原因是,如果正确完成,可配置值具有非常具体、有据可查的效果,任何无效值或损坏的文件结构都将被该程序。
然而,经常发生的情况是,配置文件结构本身就变成了一种编程语言,包含控制流和所有东西——文档记录很差,有一个古怪的语法和解析器,只有团队中最有经验的开发人员才能使用它可以触摸而不会完全破坏应用程序。
因此,在像 Scheme 这样的语言中,甚至代码也被视为一等数据。您可以像对待其他代码一样对待函数和 lambda 表达式,比如将它们传递给其他函数和 lambda 表达式。我建议继续阅读文本,因为这一切都会变得非常清楚。
这是您应该通过在编译器中编写来理解的。
编译器中的一个常见步骤是将程序转换为抽象语法树。表示通常类似于树,例如 [+, 2, 3],其中 + 是根,2, 3 是孩子。
Lisp 语言只是将其视为其数据。因此,数据和代码之间没有分离,它们都是看起来像 AST 树的列表。
代码绝对是数据,但数据绝对不总是代码。让我们举一个基本的例子——客户名称。它与代码无关,它是功能性(必要的),而不是应用程序的技术(偶然)方面。
您可能会说任何技术/意外数据都是代码,而功能/基本数据不是。