<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>元循环求值 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E5%85%83%E5%BE%AA%E7%8E%AF%E6%B1%82%E5%80%BC/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Tue, 08 Aug 2023 10:40:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E5%85%83%E5%BE%AA%E7%8E%AF%E6%B1%82%E5%80%BC/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>《SICP》笔记：计算机程序的构造和解释，以及编程世界的“无所住而生其心”</title>
      <link>https://ftmao.com/blog/bookstore/sicp-book-notes/</link>
      <pubDate>Tue, 08 Aug 2023 10:40:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/sicp-book-notes/</guid>
      <description>&lt;h1 id=&#34;sicp笔记计算机程序的构造和解释以及编程世界的无所住而生其心&#34;&gt;《SICP》笔记：计算机程序的构造和解释，以及编程世界的“无所住而生其心”&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;目标读者：写过几年代码、用过不止一门语言、想知道“编程这件事的本质到底是什么”的程序员。&lt;br&gt;
参考书籍：Harold Abelson &amp;amp; Gerald Jay Sussman, &lt;em&gt;Structure and Interpretation of Computer Programs&lt;/em&gt;（SICP，中译《计算机程序的构造和解释》）&lt;br&gt;
阅读目标：不求逐章复述，而是提炼一条主线——&lt;strong&gt;过程与数据的对偶&lt;/strong&gt;，看清抽象、求值、解释器这三件事如何环环相扣。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;0-写在前面为什么这本书被称为-cs-的佛经&#34;&gt;0. 写在前面：为什么这本书被称为 CS 的“佛经”&lt;/h2&gt;
&lt;p&gt;SICP 是 MIT 的传奇教材。1980 年代到 2000 年代，MIT 的计算机导论课 6.001 用它做教材：第一堂课不教语法，直接写求平方根的牛顿法；第一学期结束，学生要&lt;strong&gt;用 Scheme 写出一个 Scheme 解释器&lt;/strong&gt;。这门课出了名地难，也出了名地改变人——无数程序员回忆它是“第一次觉得编程是一门手艺，而不是一门手艺活”的时刻。&lt;/p&gt;
&lt;p&gt;几个背景事实，帮你定位这本书：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;书出版于 1985 年（第一版），语言用的是 Lisp 方言 &lt;strong&gt;Scheme&lt;/strong&gt;。Lisp 诞生于 1958 年（John McCarthy），是现存第二老的高级语言（第一是 Fortran，1957 年）。&lt;/li&gt;
&lt;li&gt;2008 年 MIT 把 6.001 换成了 Python 课程。Sussman 本人的解释是：现在工程师更多是在“把不理解的黑盒零件拼起来”，而不是从零构造系统——时代变了。但 SICP 的地位反而因此更稳固：它不再是一门课的教材，而是程序员自我修炼的经典。&lt;/li&gt;
&lt;li&gt;Paul Graham（Y Combinator 创始人）1995 年用 Lisp 写了 Viaweb——史上最早的 Web 应用之一，1998 年被雅虎以约 4900 万美元收购。他后来写《黑客与画家》，反复鼓吹 Lisp 的威力，把 SICP 式的抽象能力说成是 Viaweb 能以 3 个人对抗几十人团队的“秘密武器”。这个故事后面还会用到。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这本书为什么这么神？一句话：它讲的不是“怎么写代码”，而是**“计算是什么”**。全书有一条暗线：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;计算的本质 = 过程（process）与数据（data）的对偶。程序在二者之间不断转换：数据可以被当作过程执行，过程也可以被当作数据传递。到书的结尾，我们用一个程序（求值器）把语言自己解释了一遍——这就是编程世界的“言语道断，心行处灭”。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个博客另有佛学笔记，读完全文你会发现，SICP 的气质确实和《金刚经》那句“应无所住而生其心”隐隐相通：&lt;strong&gt;不执着于任何固定的表示（无所住），才能自由地在过程与数据之间切换（生其心）&lt;/strong&gt;。但先把技术内容讲扎实，佛的部分放最后。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;1-一句话理解-sicp抽象的三层结构&#34;&gt;1. 一句话理解 SICP：抽象的三层结构&lt;/h2&gt;
&lt;p&gt;SICP 开篇就给出了“什么是编程语言”的定义。任何一门强大的语言，必须提供三样东西：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;基本表达（primitive expressions）&lt;/strong&gt;：语言提供的最小积木，比如数字 &lt;code&gt;2&lt;/code&gt;、运算符 &lt;code&gt;+&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;组合手段（means of combination）&lt;/strong&gt;：把简单东西拼成复杂东西，比如 &lt;code&gt;(+ 2 3)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;抽象手段（means of abstraction）&lt;/strong&gt;：给复杂东西命名，然后像用基本积木一样用它，比如 &lt;code&gt;(define (square x) (* x x))&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;一句话理解全书：&lt;strong&gt;SICP 就是在“过程”和“数据”两个战场上，反复演示这三层结构怎么玩。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第 1 章：过程的抽象（用函数表达计算过程）。&lt;/li&gt;
&lt;li&gt;第 2 章：数据的抽象（用构造函数/选择函数把表示藏起来）。&lt;/li&gt;
&lt;li&gt;第 3 章：模块化、对象与状态（引入赋值后，代换模型失效，必须换环境模型）。&lt;/li&gt;
&lt;li&gt;第 4 章：元语言抽象（写一个求值器，用语言解释语言）。&lt;/li&gt;
&lt;li&gt;第 5 章：寄存器机器（解释器如何在真实机器上跑起来）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;读 SICP 的正确姿势，不是记知识点，而是看作者怎么&lt;strong&gt;用同一把刀（三层抽象）切开五个不同的问题&lt;/strong&gt;。这把刀，就是这本书真正想传给你的东西。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-过程抽象牛顿法开方sicp-的第一个当头棒喝&#34;&gt;2. 过程抽象：牛顿法开方，SICP 的第一个“当头棒喝”&lt;/h2&gt;
&lt;p&gt;6.001 第一堂课就讲平方根。这不是随便选的例子——它完整演示了“一个数学定义（声明性知识）”和“一个计算过程（操作性知识）”之间的鸿沟，而弥合这个鸿沟，正是编程的本质。&lt;/p&gt;
&lt;h3 id=&#34;21-数学定义--计算过程&#34;&gt;2.1 数学定义 ≠ 计算过程&lt;/h3&gt;
&lt;p&gt;数学上，$\sqrt{x}$ 的定义是：“满足 $y^2 = x$ 且 $y \ge 0$ 的那个 $y$”。这个定义完美，但&lt;strong&gt;不能算&lt;/strong&gt;——它没告诉你怎么找到这个 $y$。&lt;/p&gt;
&lt;p&gt;牛顿法（Newton&amp;rsquo;s method，SICP 中归为 Heron of Alexandria 的巴比伦法特例）给出的是一个过程：从任意猜测 $y$ 出发，反复取 $y$ 和 $x/y$ 的平均值作为新猜测，直到足够接近：&lt;/p&gt;
&lt;p&gt;$$
y_{n+1} = \frac{y_n + x / y_n}{2}
$$&lt;/p&gt;
&lt;h3 id=&#34;22-用小数字完整走一遍求-sqrt2&#34;&gt;2.2 用小数字完整走一遍：求 $\sqrt{2}$&lt;/h3&gt;
&lt;p&gt;从猜测 $y_0 = 1$ 开始：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;迭代&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;当前猜测 $y_n$&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;$x / y_n$&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;新猜测（取平均）&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;1&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;2&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;(1 + 2) / 2 = &lt;strong&gt;1.5&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;1&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;1.5&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;1.3333&amp;hellip;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;(1.5 + 1.3333) / 2 = &lt;strong&gt;1.4167&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;2&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;1.4167&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;1.4118&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;(1.4167 + 1.4118) / 2 = &lt;strong&gt;1.4142&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;三步就到 1.4142，误差已经小于 $10^{-4}$。真实值是 1.41421356&amp;hellip;。收敛速度是平方级的——每迭代一次，有效位数大约翻倍。&lt;/p&gt;
&lt;h3 id=&#34;23-scheme-实现嵌套定义把复杂性藏起来&#34;&gt;2.3 Scheme 实现：嵌套定义把复杂性藏起来&lt;/h3&gt;
&lt;p&gt;SICP 把牛顿法写成一组互相调用的过程，最后用 &lt;code&gt;define&lt;/code&gt; 的内部嵌套把辅助过程全部藏起来：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-scheme&#34; data-lang=&#34;scheme&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(sqrt x)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;good-enough?&lt;/span&gt; guess)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&amp;lt; (abs (- (&lt;span style=&#34;color:#a6e22e&#34;&gt;square&lt;/span&gt; guess) x)) &lt;span style=&#34;color:#ae81ff&#34;&gt;0.001&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;improve&lt;/span&gt; guess)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#a6e22e&#34;&gt;average&lt;/span&gt; guess (/ x guess)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sqrt-iter&lt;/span&gt; guess)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#66d9ef&#34;&gt;if &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;good-enough?&lt;/span&gt; guess)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        guess
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;sqrt-iter&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;improve&lt;/span&gt; guess))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#a6e22e&#34;&gt;sqrt-iter&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;注意这里发生了什么：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户只看到 &lt;code&gt;(sqrt 2)&lt;/code&gt;，&lt;strong&gt;不用知道里面有迭代、有容忍度、有牛顿公式&lt;/strong&gt;。这就是过程抽象：&lt;strong&gt;把一个过程当作黑盒，用名字调用它。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;辅助过程 &lt;code&gt;good-enough?&lt;/code&gt;、&lt;code&gt;improve&lt;/code&gt;、&lt;code&gt;sqrt-iter&lt;/code&gt; 定义在 &lt;code&gt;sqrt&lt;/code&gt; 内部，外面的代码根本看不见它们，而且它们直接共享参数 &lt;code&gt;x&lt;/code&gt;，不用层层传递。&lt;/li&gt;
&lt;li&gt;这是 SICP 给新人的第一棒：&lt;strong&gt;函数不是用来“复用代码”的，是用来“命名计算过程、压制细节”的。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;避坑提示：SICP 练习 1.7 指出，&lt;code&gt;good-enough?&lt;/code&gt; 用固定阈值 &lt;code&gt;0.001&lt;/code&gt; 对很小的数（如 &lt;code&gt;sqrt 0.0001&lt;/code&gt;）和很大的数（如 &lt;code&gt;sqrt 1e13&lt;/code&gt;）都会出错——前者精度不够，后者因为浮点精度问题可能不收敛。工程上的修法是判断“新旧猜测的相对变化量是否足够小”，而不是绝对误差。这个练习至今仍是理解“数值代码里的坑”的最佳入门。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;3-高阶函数sumintegral-通用模板抽象出模式本身&#34;&gt;3. 高阶函数：sum/integral 通用模板，抽象出“模式”本身&lt;/h2&gt;
&lt;h3 id=&#34;31-三个长得像的求和&#34;&gt;3.1 三个长得像的求和&lt;/h3&gt;
&lt;p&gt;SICP 第 1 章的下一个重点：数学家早就发现一堆求和过程长得几乎一样，只是“每一项怎么算”和“下一项怎么来”不同：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-scheme&#34; data-lang=&#34;scheme&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; 从 a 加到 b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sum-integers&lt;/span&gt; a b)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;if &lt;/span&gt;(&amp;gt; a b) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; (+ a (&lt;span style=&#34;color:#a6e22e&#34;&gt;sum-integers&lt;/span&gt; (+ a &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) b)))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; 从 a 的立方加到 b 的立方&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sum-cubes&lt;/span&gt; a b)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;if &lt;/span&gt;(&amp;gt; a b) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; (+ (&lt;span style=&#34;color:#a6e22e&#34;&gt;cube&lt;/span&gt; a) (&lt;span style=&#34;color:#a6e22e&#34;&gt;sum-cubes&lt;/span&gt; (+ a &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) b)))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; 莱布尼茨级数求 π：1/1 - 1/3 + 1/5 - ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;pi-sum&lt;/span&gt; a b)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;if &lt;/span&gt;(&amp;gt; a b) &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; (+ (/ &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt; (* a (+ a &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;))) (&lt;span style=&#34;color:#a6e22e&#34;&gt;pi-sum&lt;/span&gt; (+ a &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;) b))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;三个过程的骨架一模一样。SICP 的做法：把骨架抽出来，&lt;strong&gt;让“每一项怎么算（term）”和“下一项怎么来（next）”变成参数&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-scheme&#34; data-lang=&#34;scheme&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sum&lt;/span&gt; term a next b)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;if &lt;/span&gt;(&amp;gt; a b)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (+ (&lt;span style=&#34;color:#a6e22e&#34;&gt;term&lt;/span&gt; a) (&lt;span style=&#34;color:#a6e22e&#34;&gt;sum&lt;/span&gt; term (&lt;span style=&#34;color:#a6e22e&#34;&gt;next&lt;/span&gt; a) next b))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; π 级数就剩两行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;pi-sum&lt;/span&gt; a b)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#a6e22e&#34;&gt;sum&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;) (/ &lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt; (* x (+ x &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;))))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       a
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       (&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;) (+ x &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       b))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;code&gt;sum&lt;/code&gt; 就是一个&lt;strong&gt;高阶函数（higher-order procedure）&lt;/strong&gt;：以过程为参数、以过程为返回值的过程。&lt;/p&gt;
&lt;h3 id=&#34;32-一句话理解高阶函数&#34;&gt;3.2 一句话理解高阶函数&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;高阶函数 = 把“算法中的变化点”变成参数。你传进去的不是数据，是行为。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果你觉得眼熟，那就对了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你在 JavaScript 里写的 &lt;code&gt;array.map(fn)&lt;/code&gt;、&lt;code&gt;array.filter(fn)&lt;/code&gt;、&lt;code&gt;array.reduce(fn, init)&lt;/code&gt;，就是 &lt;code&gt;sum&lt;/code&gt; 的亲戚。&lt;/li&gt;
&lt;li&gt;Python 的 &lt;code&gt;sorted(lst, key=fn)&lt;/code&gt;、Java 的 &lt;code&gt;Comparator&lt;/code&gt;，传的都是“行为参数”。&lt;/li&gt;
&lt;li&gt;数值积分 $\int_a^b f \approx \sum f(x),dx$ 在 SICP 里就是 &lt;code&gt;sum&lt;/code&gt; 换一套 &lt;code&gt;term/next&lt;/code&gt; 的事——积分器和求和器共享同一个骨架，这在数学上是黎曼和，在代码上就是高阶函数。&lt;/li&gt;
&lt;li&gt;设计模式里的 Strategy、模板方法，本质都是“手写的高阶函数”——语言不支持把函数当值传递，就只能用类和接口模拟。这也是为什么 Peter Norvig 那篇著名文章指出：GoF 23 个设计模式里有 16 个在有头等函数的语言里“要么消失、要么简化到看不见”。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;33-lambda-与-let不命名也是自由&#34;&gt;3.3 lambda 与 let：不命名，也是自由&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;(lambda (x) (* x x))&lt;/code&gt; 造出一个匿名过程。SICP 特意强调：能给过程命名（&lt;code&gt;define&lt;/code&gt;）当然好，但&lt;strong&gt;能不命名也同样重要&lt;/strong&gt;——很多小过程只用一次，为它发明一个名字反而污染命名空间。&lt;/p&gt;
&lt;p&gt;工程对照：这就是你写 &lt;code&gt;list.sort(key=lambda x: x.age)&lt;/code&gt; 时干的事。&lt;code&gt;let&lt;/code&gt; 则只是 lambda 的语法糖：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-scheme&#34; data-lang=&#34;scheme&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;let &lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (+ a b))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; 等价于&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;((&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt; b) (+ a b)) &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;一句话理解：&lt;code&gt;let&lt;/code&gt; 不是“定义变量的特殊语法”，而是“立刻调用一个匿名函数”。变量绑定就是函数参数绑定。这个视角在第 4 章写求值器时会回报你——&lt;code&gt;let&lt;/code&gt; 根本不用实现，翻译成 lambda 就完了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;4-数据抽象有理数例子和抽象屏障这道墙&#34;&gt;4. 数据抽象：有理数例子，和“抽象屏障”这道墙&lt;/h2&gt;
&lt;h3 id=&#34;41-用之前先假装它存在&#34;&gt;4.1 用之前，先假装它存在&lt;/h3&gt;
&lt;p&gt;SICP 第 2 章开篇的方法论非常反直觉：&lt;strong&gt;先有“愿望思维”（wishful thinking）——假设有理数的构造函数和选择函数已经存在，先把用它们的上层代码写完，再回头实现它们。&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-scheme&#34; data-lang=&#34;scheme&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; 假设这三个东西存在：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; (make-rat n d)  → 构造一个 n/d 的有理数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; (numer x)       → 取分子&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; (denom x)       → 取分母&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; 于是加法直接写出来：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;add-rat&lt;/span&gt; x y)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#a6e22e&#34;&gt;make-rat&lt;/span&gt; (+ (* (&lt;span style=&#34;color:#a6e22e&#34;&gt;numer&lt;/span&gt; x) (&lt;span style=&#34;color:#a6e22e&#34;&gt;denom&lt;/span&gt; y))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               (* (&lt;span style=&#34;color:#a6e22e&#34;&gt;numer&lt;/span&gt; y) (&lt;span style=&#34;color:#a6e22e&#34;&gt;denom&lt;/span&gt; x)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (* (&lt;span style=&#34;color:#a6e22e&#34;&gt;denom&lt;/span&gt; x) (&lt;span style=&#34;color:#a6e22e&#34;&gt;denom&lt;/span&gt; y))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;用小数字验证：$\frac{1}{2} + \frac{1}{3} = \frac{1\times3 + 1\times2}{2\times3} = \frac{5}{6}$。对。&lt;/p&gt;
&lt;p&gt;关键点：&lt;code&gt;add-rat&lt;/code&gt; 的代码里&lt;strong&gt;没有任何一处依赖“有理数内部怎么存”&lt;/strong&gt;。它只依赖 &lt;code&gt;make-rat&lt;/code&gt;/&lt;code&gt;numer&lt;/code&gt;/&lt;code&gt;denom&lt;/code&gt; 这三个接口承诺的行为。这条接口，SICP 称之为&lt;strong&gt;抽象屏障（abstraction barrier）&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;42-屏障之下表示随便换&#34;&gt;4.2 屏障之下，表示随便换&lt;/h3&gt;
&lt;p&gt;因为有屏障，下面这些是&lt;strong&gt;可以在不改变 &lt;code&gt;add-rat&lt;/code&gt; 一行的前提下&lt;/strong&gt;完成的修改：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;存原始分子分母，约分留给打印时做；&lt;/li&gt;
&lt;li&gt;在构造时就约分（SICP 练习 2.1：用 &lt;code&gt;gcd&lt;/code&gt; 化简）；&lt;/li&gt;
&lt;li&gt;保证分母恒为正，把符号统一挪到分子。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就是数据抽象的工程价值：&lt;strong&gt;表示的修改被关进笼子，使用方无感知。&lt;/strong&gt; 你在工程里天天享受这个红利：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;java.util.List&lt;/code&gt; 背后是 &lt;code&gt;ArrayList&lt;/code&gt; 还是 &lt;code&gt;LinkedList&lt;/code&gt;，调用方不该关心；&lt;/li&gt;
&lt;li&gt;HTTP API 的响应字段内部存 JSON 还是 protobuf，客户端不该关心；&lt;/li&gt;
&lt;li&gt;数据库的存储引擎从 MyISAM 换 InnoDB，SQL 不变。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;一句话理解：数据抽象不是“封装”这个 OOP 词汇的专利，它是更根本的原则——&lt;strong&gt;“用一组行为约定代替对表示的直接依赖”&lt;/strong&gt;。Java 的接口、C 的 opaque pointer、Rust 的 trait，全是这道墙的不同砌法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;5-conscarcdr-与闭包性质一对盒子造出整个数据世界&#34;&gt;5. cons/car/cdr 与闭包性质：一对盒子造出整个数据世界&lt;/h2&gt;
&lt;h3 id=&#34;51-唯一的胶水cons&#34;&gt;5.1 唯一的胶水：cons&lt;/h3&gt;
&lt;p&gt;Scheme 的数据组合手段只有一个：&lt;code&gt;cons&lt;/code&gt;。&lt;code&gt;(cons 1 2)&lt;/code&gt; 造出一个序对（pair），&lt;code&gt;car&lt;/code&gt; 取左，&lt;code&gt;cdr&lt;/code&gt; 取右。就这一个盒子，SICP 搭出了列表、树、表、集合……全部数据结构。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-scheme&#34; data-lang=&#34;scheme&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(cons &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; (cons &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; (cons &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; nil)))   &lt;span style=&#34;color:#75715e&#34;&gt;; 这就是列表 (1 2 3)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(cons (cons &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;) (cons &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;))     &lt;span style=&#34;color:#75715e&#34;&gt;; 这就是一棵二叉树&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;为什么够了？因为 SICP 给出了&lt;strong&gt;闭包性质（closure property）&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;组合手段满足闭包性质 = 组合出来的东西，本身还能继续被同样的手段组合。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;cons 出来的序对，可以再被 cons。这一句话是数据世界的“递归基”：列表是嵌套的 cons，树是嵌套的 cons，任何层级结构都是嵌套的 cons。&lt;/p&gt;
&lt;p&gt;工程对照：HTML 的 DOM 树满足闭包性质（元素套元素）；Unix 管道满足闭包性质（任何命令的输出都能进下一个命令的输入）；JSON 满足闭包性质（对象里套对象）。&lt;strong&gt;满足闭包性质的组合手段，组合成本不随复杂度增长&lt;/strong&gt;——这是 SICP 对“为什么 Lisp 和 Unix 都能小核心、大生态”的统一解释。&lt;/p&gt;
&lt;h3 id=&#34;52-最炸裂的一课cons-可以不用数据实现&#34;&gt;5.2 最炸裂的一课：cons 可以不用数据实现&lt;/h3&gt;
&lt;p&gt;SICP 2.1.3 有一个让无数人第一次头皮发麻的段落：&lt;code&gt;cons&lt;/code&gt;、&lt;code&gt;car&lt;/code&gt;、&lt;code&gt;cdr&lt;/code&gt; 可以&lt;strong&gt;只用过程（函数）实现，不用任何数据结构&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-scheme&#34; data-lang=&#34;scheme&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(cons x y)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt;) (&lt;span style=&#34;color:#a6e22e&#34;&gt;m&lt;/span&gt; x y)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(car z)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#a6e22e&#34;&gt;z&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; q) p)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(cdr z)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#a6e22e&#34;&gt;z&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;p&lt;/span&gt; q) q)))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;走一遍 &lt;code&gt;(car (cons 1 2))&lt;/code&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;(cons 1 2)&lt;/code&gt; 返回一个匿名函数，它等着别人告诉它“你要左还是要右”。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(car z)&lt;/code&gt; 把 &lt;code&gt;(lambda (p q) p)&lt;/code&gt; 塞给它，意思是“给我左边那个”。&lt;/li&gt;
&lt;li&gt;结果：&lt;code&gt;1&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这意味着什么？&lt;strong&gt;“数据”和“过程”的边界是虚构的。&lt;/strong&gt; 一个序对可以完全用行为（“当被问到时左右各报什么”）来定义，不占用任何存储意义上的“盒子”。这就是第 0 节说的对偶性第一次显形：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;数据是被冻住的过程；过程是活过来的数据。&lt;/strong&gt; 佛家说“色即是空，空即是色”，SICP 说“数据即过程，过程即数据”——结构上是同一个句式。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;（历史彩蛋：这套“用函数编码数据”的玩法不是 SICP 发明的，是 Alonzo Church 1930 年代 λ 演算里的 Church 编码——连自然数都可以用纯函数定义。SICP 把它拿来当头棒喝用。）&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;6-求值的两个模型代换模型优雅环境模型救命&#34;&gt;6. 求值的两个模型：代换模型优雅，环境模型救命&lt;/h2&gt;
&lt;h3 id=&#34;61-代换模型把函数调用当代数化简&#34;&gt;6.1 代换模型：把函数调用当代数化简&lt;/h3&gt;
&lt;p&gt;求值一个组合式，最朴素的模型是&lt;strong&gt;代换模型（substitution model）&lt;/strong&gt;：把形参全部替换成实参，然后化简。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(square (+ 2 3))
→ (square 5)
→ (* 5 5)
→ 25&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这个模型简单、优雅，而且可以直接证明程序正确性（这正是函数式编程“可推导”的理论基础）。SICP 用它讲清了**应用序（先算实参再代入）&lt;strong&gt;与&lt;/strong&gt;正则序（先代入再按需算）**的区别——后者就是第 7 节惰性求值的种子。&lt;/p&gt;
&lt;h3 id=&#34;62-赋值一来代换模型当场死亡&#34;&gt;6.2 赋值一来，代换模型当场死亡&lt;/h3&gt;
&lt;p&gt;第 3 章引入 &lt;code&gt;set!&lt;/code&gt;（赋值）之后，代换模型立刻崩溃。考虑一个取钱过程：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-scheme&#34; data-lang=&#34;scheme&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;balance &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;withdraw&lt;/span&gt; amount)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#66d9ef&#34;&gt;set! &lt;/span&gt;balance (- balance amount))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  balance)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;code&gt;(withdraw 25)&lt;/code&gt; 得 75，再 &lt;code&gt;(withdraw 25)&lt;/code&gt; 得 50。同样的表达式、同样的参数，两次结果不同——代换模型的基石“相同表达式有相同值”（引用透明性）碎了。表达式不再是值的替身，&lt;strong&gt;时间&lt;/strong&gt;进入了程序：值取决于“你什么时候问”。&lt;/p&gt;
&lt;h3 id=&#34;63-环境模型变量不再是值而是在某个环境里的格子&#34;&gt;6.3 环境模型：变量不再“是”值，而是“在”某个环境里的格子&lt;/h3&gt;
&lt;p&gt;替代方案是&lt;strong&gt;环境模型（environment model）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;环境 = 一串框架（frame）&lt;/strong&gt;，每个框架是一张“变量名 → 值”的表，框架之间用指针连成链，最后终结于全局环境。&lt;/li&gt;
&lt;li&gt;变量求值 = 从当前框架往外一层层找，找到第一个绑定。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lambda&lt;/code&gt; 求值 = 造出一个过程对象，它携带两样东西：&lt;strong&gt;参数+函数体&lt;/strong&gt;，和&lt;strong&gt;一个指向定义时环境的指针&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;调用过程 = 建一个新框架，把实参绑进去，新框架的外层指针指向过程对象里存的那个环境。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    GE[&amp;#34;全局环境&amp;lt;br/&amp;gt;balance = 50&amp;lt;br/&amp;gt;withdraw = 过程对象&amp;#34;]
    PO[&amp;#34;过程对象&amp;lt;br/&amp;gt;参数: amount&amp;lt;br/&amp;gt;函数体: set! balance ...&amp;lt;br/&amp;gt;环境指针 ──→&amp;#34;]
    E1[&amp;#34;框架 E1&amp;lt;br/&amp;gt;amount = 25&amp;#34;]
    PO --&amp;gt; GE
    E1 --&amp;gt;|&amp;#34;外层指针&amp;#34;| GE
    GE -.-&amp;gt;|&amp;#34;调用 withdraw 25 时创建&amp;#34;| E1&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;为什么函数式程序员迷恋“纯函数、不可变数据”？SICP 给了最诚实的答案：&lt;strong&gt;不是因为美，而是因为一旦引入赋值，你从此失去了代换模型这个思维工具，必须用环境模型这种更贵的心智模型去追每一个变量的历史。&lt;/strong&gt; 工程对照：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redux 的“单一不可变 state 树”，本质是在 JS 里人工维持代换模型可用；&lt;/li&gt;
&lt;li&gt;Rust 的所有权与借用检查，是编译器帮你控制“谁在什么时候能改什么”；&lt;/li&gt;
&lt;li&gt;并发 bug 的根源几乎都是“共享可变状态”——环境模型里那条指针链，到了多线程就是一场灾难。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;避坑提示：闭包捕获的是&lt;strong&gt;环境（变量的格子）而不是当时的值&lt;/strong&gt;。经典面试题——Python 里 &lt;code&gt;fns = [lambda: i for i in range(3)]&lt;/code&gt;，三个函数都返回 2——就是这句话的具象化。JS 的 &lt;code&gt;var&lt;/code&gt; 循环、Go 的 goroutine 循环变量（Go 1.22 之前的坑）都是同一原理。学完环境模型，这类 bug 你一眼看穿。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;7-流与惰性求值用无穷的数据结构把时间装回瓶子&#34;&gt;7. 流与惰性求值：用“无穷的数据结构”把时间装回瓶子&lt;/h2&gt;
&lt;h3 id=&#34;71-问题状态和赋值污染了一切能不要吗&#34;&gt;7.1 问题：状态和赋值污染了一切，能不要吗？&lt;/h3&gt;
&lt;p&gt;第 3 章结尾 SICP 问了一个漂亮的反问：我们引入赋值是为了建模“随时间变化的状态”（比如银行账户余额）。但换个视角——&lt;strong&gt;余额不是“一个会变的格子”，而是“一个无穷序列：每次交易后的全部余额历史”&lt;/strong&gt;。变化的东西，可以被重新表示成一个&lt;strong&gt;不变但无穷&lt;/strong&gt;的东西。&lt;/p&gt;
&lt;p&gt;问题是：无穷的序列怎么在有限的内存里表示？答案：&lt;strong&gt;只在你看它的时候才算它&lt;/strong&gt;——这就是&lt;strong&gt;流（stream）&lt;strong&gt;和&lt;/strong&gt;惰性求值（lazy evaluation）&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;72-流的实现cons-的另一半推迟到以后&#34;&gt;7.2 流的实现：cons 的另一半推迟到以后&lt;/h3&gt;
&lt;p&gt;流的全部魔法在一对操作里（SICP 用 &lt;code&gt;cons-stream&lt;/code&gt; 宏和 &lt;code&gt;delay&lt;/code&gt;/&lt;code&gt;force&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(cons-stream a b)&lt;/code&gt;：&lt;code&gt;a&lt;/code&gt; 立刻算，&lt;code&gt;b&lt;/code&gt; &lt;strong&gt;不&lt;/strong&gt;算，包成一个“承诺”（promise）存起来；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(stream-cdr s)&lt;/code&gt;：兑现那个承诺（force），真正算出下一截。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是无穷斐波那契流只需要三行：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-scheme&#34; data-lang=&#34;scheme&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;define &lt;/span&gt;fibs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  (&lt;span style=&#34;color:#a6e22e&#34;&gt;cons-stream&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#a6e22e&#34;&gt;cons-stream&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      (&lt;span style=&#34;color:#a6e22e&#34;&gt;add-streams&lt;/span&gt; fibs (&lt;span style=&#34;color:#a6e22e&#34;&gt;stream-cdr&lt;/span&gt; fibs)))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;读法：&lt;code&gt;fibs&lt;/code&gt; = 0 开头，接着 1，接着是“fibs 和它自己的错位相加”。用小数字走一遍错位相加：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fibs:           0, 1, 1, 2, 3, 5,  8, ...
(stream-cdr fibs): 1, 1, 2, 3, 5, 8, 13, ...
对应位相加:        1, 2, 3, 5, 8, 13, ...&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;从第 3 位开始，每一位都是前两段对应位相加的结果——正是斐波那契定义。但注意：&lt;strong&gt;这个流并没有预先存在，是你每取一个元素，它才长出一个元素&lt;/strong&gt;。定义是递归的，数据却是按需生长的。&lt;/p&gt;
&lt;h3 id=&#34;73-流处理管道信号处理式的程序结构&#34;&gt;7.3 流处理管道：信号处理式的程序结构&lt;/h3&gt;
&lt;p&gt;有了流，程序可以写成信号处理系统的样子：生成器 → 变换器 → 过滤器 → 累积器，一环一环接起来。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    G[&amp;#34;生成器&amp;lt;br/&amp;gt;无穷整数流&amp;lt;br/&amp;gt;2, 3, 4, 5, ...&amp;#34;] --&amp;gt; F1[&amp;#34;过滤器&amp;lt;br/&amp;gt;筛掉被 2 整除的&amp;#34;]
    F1 --&amp;gt; F2[&amp;#34;过滤器&amp;lt;br/&amp;gt;筛掉被 3 整除的&amp;#34;]
    F2 --&amp;gt; F3[&amp;#34;过滤器&amp;lt;br/&amp;gt;筛掉被 5 整除的&amp;#34;]
    F3 --&amp;gt; OUT[&amp;#34;输出&amp;lt;br/&amp;gt;素数流&amp;lt;br/&amp;gt;2, 3, 5, 7, 11, ...&amp;#34;]
    style OUT fill:#e8f5e9&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;上图就是 SICP 的&lt;strong&gt;埃氏筛素数流&lt;/strong&gt;：每个素数一旦露面，就派生出一个新过滤器挂在管道上，把它的倍数全部筛掉。整个程序是对“全体素数”这个无穷集合的操作，但任意时刻内存里只有用到的那一小段。&lt;/p&gt;
&lt;p&gt;工程对照，这个思想你今天天天在用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unix 管道&lt;/strong&gt;：&lt;code&gt;cat access.log | grep 500 | wc -l&lt;/code&gt;——三段程序用惰性流串起来，谁也不会把整个日志读进内存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java 8 Streams / RxJava / Rust 的 Iterator&lt;/strong&gt;：&lt;code&gt;stream.filter(...).map(...).collect()&lt;/code&gt;，中间操作全部惰性，终端操作才触发计算。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python 生成器&lt;/strong&gt;：&lt;code&gt;yield&lt;/code&gt; 就是 &lt;code&gt;delay&lt;/code&gt;，&lt;code&gt;next()&lt;/code&gt; 就是 &lt;code&gt;force&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;大数据框架&lt;/strong&gt;（Flink/Spark Streaming）：整个流处理行业就是 SICP 第 3.5 节的工业化。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;一句话理解：流 = &lt;strong&gt;用“无穷的静态结构”替代“变化的状态”&lt;/strong&gt;。赋值把“值随时间变”摊给了程序员的心智；流把同样的现象表示成一个数学对象，时间被折叠进“取到第几个元素”这个参数里。这是 FP 社区对抗复杂性的核心武器之一。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;8-元循环求值器用语言解释语言全书的高潮&#34;&gt;8. 元循环求值器：用语言解释语言，全书的高潮&lt;/h2&gt;
&lt;h3 id=&#34;81-为什么要写求值器&#34;&gt;8.1 为什么要写求值器&lt;/h3&gt;
&lt;p&gt;第 4 章，SICP 做了一件别的教材不敢在入门书里做的事：&lt;strong&gt;用 Scheme 写一个 Scheme 解释器&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个解释器叫&lt;strong&gt;元循环求值器（metacircular evaluator）&lt;/strong&gt;：“元”因为它在谈论语言自身，“循环”因为被解释的语言和实现它的语言是同一个。为什么这是高潮？因为前四章一直在说“过程与数据可以对偶转换”，而&lt;strong&gt;求值器是这个命题的终极证明&lt;/strong&gt;：你的程序，对解释器来说只是数据（一个列表结构）；而解释器本身，也只是一个程序。&lt;/p&gt;
&lt;h3 id=&#34;82-evalapply-循环整个语言只剩两个函数&#34;&gt;8.2 eval/apply 循环：整个语言只剩两个函数&lt;/h3&gt;
&lt;p&gt;剥掉所有细节，求值器的心脏是两个互相递归的函数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;eval（表达式，环境）&lt;/strong&gt;：对表达式分派——
&lt;ul&gt;
&lt;li&gt;自求值的（数字、字符串）→ 直接返回；&lt;/li&gt;
&lt;li&gt;变量 → 去环境里查；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(quote x)&lt;/code&gt; → 返回 x 本身；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(define ...)&lt;/code&gt; / &lt;code&gt;(set! ...)&lt;/code&gt; → 改环境；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(if ...)&lt;/code&gt; → 先 eval 谓词，再选一支；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(lambda ...)&lt;/code&gt; → 打包成“过程对象”（参数 + 函数体 + 当前环境指针，正是第 6 节环境模型里的那个结构）；&lt;/li&gt;
&lt;li&gt;函数调用 → 先 eval 运算符得到过程，再把所有实参 eval 出来，交给 &lt;strong&gt;apply&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;apply（过程，实参列表）&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;基本过程（&lt;code&gt;+&lt;/code&gt;、&lt;code&gt;car&lt;/code&gt; 这种内置的）→ 直接调用底层实现；&lt;/li&gt;
&lt;li&gt;复合过程（用户用 lambda 定义的）→ 建一个新框架，形参绑实参，把过程体在这个新环境里交给 &lt;strong&gt;eval&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    E[&amp;#34;eval(表达式, 环境)&amp;#34;] --&amp;gt; Q{&amp;#34;表达式是什么类型?&amp;#34;}
    Q --&amp;gt;|&amp;#34;数字/字符串&amp;#34;| R1[&amp;#34;直接返回&amp;#34;]
    Q --&amp;gt;|&amp;#34;变量&amp;#34;| R2[&amp;#34;沿环境链查找绑定&amp;#34;]
    Q --&amp;gt;|&amp;#34;quote&amp;#34;| R3[&amp;#34;返回被引用的数据本身&amp;#34;]
    Q --&amp;gt;|&amp;#34;lambda&amp;#34;| R4[&amp;#34;打包过程对象:&amp;lt;br/&amp;gt;参数+函数体+环境指针&amp;#34;]
    Q --&amp;gt;|&amp;#34;if / define / set!&amp;#34;| R5[&amp;#34;按规则处理&amp;lt;br/&amp;gt;(子表达式递归调 eval)&amp;#34;]
    Q --&amp;gt;|&amp;#34;函数调用&amp;#34;| R6[&amp;#34;eval 运算符得过程&amp;lt;br/&amp;gt;逐个 eval 实参&amp;#34;]
    R6 --&amp;gt; A[&amp;#34;apply(过程, 实参)&amp;#34;]
    A --&amp;gt; Q2{&amp;#34;过程是哪种?&amp;#34;}
    Q2 --&amp;gt;|&amp;#34;基本过程&amp;#34;| R7[&amp;#34;调用底层内置实现&amp;#34;]
    Q2 --&amp;gt;|&amp;#34;复合过程&amp;#34;| R8[&amp;#34;新建框架: 形参→实参&amp;lt;br/&amp;gt;外层指针=过程携带的环境&amp;#34;]
    R8 --&amp;gt;|&amp;#34;在新环境中求值过程体&amp;#34;| E
    style E fill:#e3f2fd
    style A fill:#fff3e0&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这个图值得你盯着看五分钟：&lt;strong&gt;整个语言——所有语法、所有抽象——最后都坍缩成 eval 和 apply 的互相调用&lt;/strong&gt;。这就是“同像性（homoiconicity，代码即数据）”的兑现：因为 Scheme 代码本身就是 Scheme 列表（&lt;code&gt;(f a b)&lt;/code&gt; 既是调用，也是一个三元素列表），所以 eval 可以直接拿程序当数据拆开、分派、重组。&lt;/p&gt;
&lt;h3 id=&#34;83-这一课在工程里的投影&#34;&gt;8.3 这一课在工程里的投影&lt;/h3&gt;
&lt;p&gt;别以为元循环求值是学院派玩具。它直接对应你今天用的东西：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;每个语言运行时都是 eval/apply&lt;/strong&gt;：V8 解释 JS、CPython 执行字节码、JVM 跑字节码，本质都是“拿一段数据结构（代码），分派，执行”。JIT 只是给这个循环加缓存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQL 引擎&lt;/strong&gt;：你写的 SQL 对数据库是数据，查询规划器就是 eval，执行器就是 apply。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规则引擎、模板引擎、配置文件即代码&lt;/strong&gt;（Terraform 的 HCL、Kubernetes 的 YAML 控制器循环）：全是“程序读数据，把数据当程序跑”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 时代的镜像&lt;/strong&gt;：今天的 LLM Agent 循环——模型输出一段“计划/代码”（数据），宿主程序解释执行它（eval），结果再喂回模型——结构上和元循环求值器惊人地相似。提示工程里的 ReAct 模式，就是给 LLM 套了一个手写的 eval/apply。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;历史掌故&lt;/strong&gt;：正因为 Lisp 代码即数据，它的**宏（macro）**系统才能在编译期用 Lisp 本身变换 Lisp 代码。Paul Graham 说 Viaweb 能以 3 人团队每周上线几次、碾压竞争对手，核心就是 Lisp 宏让他们随时给语言“长”出适应业务的新语法——&lt;strong&gt;竞争对手在用语言写功能，他们在先造语言再写功能&lt;/strong&gt;。这是 SICP 第 4 章思想最著名的一次商业兑现。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;84-无穷后退以及无所住&#34;&gt;8.4 无穷后退，以及“无所住”&lt;/h3&gt;
&lt;p&gt;一个哲学问题浮出来：元循环求值器自己又是被谁求值的？——被底层的 Scheme 实现。那个实现又被谁执行？被机器。机器呢？……这就是 Sussman 说的“求值的无穷后退（infinite regress）”，像一组两面相对的镜子。&lt;/p&gt;
&lt;p&gt;SICP 的回答很佛系，也很硬核：&lt;strong&gt;后退没有终点，但有可以停下来的层级。&lt;/strong&gt; 求值器把“语言的意义”钉在了一个你能完整看懂的两页代码上，这就够了——剩下的层级，当作黑盒信任。这和第 2 节 &lt;code&gt;sqrt&lt;/code&gt; 的嵌套定义是同一把刀：&lt;strong&gt;在正确的层级上停止追问，把下层当黑盒，是驾驭复杂性的唯一办法。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;9-收个尾从-evalapply-到无所住而生其心&#34;&gt;9. 收个尾：从 eval/apply 到“无所住而生其心”&lt;/h2&gt;
&lt;p&gt;《金刚经》那句“应无所住而生其心”，粗解是：心不执着于任何固定的相（“住”），才能生起真正的觉照（“生其心”）。&lt;/p&gt;
&lt;p&gt;SICP 教的恰恰是同一个心法的技术版：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不住于表示&lt;/strong&gt;：有理数怎么存不重要，重要的是 &lt;code&gt;make-rat/numer/denom&lt;/code&gt; 的行为约定（第 4 节）；cons 可以用盒子实现，也可以用纯函数实现（第 5 节）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不住于过程与数据的分别&lt;/strong&gt;：代码可以被当数据改写（宏、同像性），数据可以被当过程执行（Church 编码、求值器）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不住于语言的边界&lt;/strong&gt;：当你能为问题造一门小语言（第 4 章元语言抽象、DSL），“用什么语言编程”这个执念就放下了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不住于模型的执念&lt;/strong&gt;：代换模型够用就别上环境模型；状态非要建模时，先试试流能不能把它变成静态结构（第 6、7 节）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;心无所住，方能在过程与数据、语言与元语言之间自由穿行。这是 SICP 作为“CS 佛经”的真正含义：&lt;strong&gt;它教的不是 Scheme，是一种对一切表示层都不执着、只忠于计算本质的编程心法。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;10-带走的清单&#34;&gt;10. 带走的清单&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;五句话记住 SICP：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;语言三要素：&lt;strong&gt;基本表达、组合手段、抽象手段&lt;/strong&gt;。评价任何语言/框架，先数这三样。&lt;/li&gt;
&lt;li&gt;过程抽象：&lt;strong&gt;给计算过程起名字，把细节关进黑盒&lt;/strong&gt;（牛顿法 sqrt 是起点）。&lt;/li&gt;
&lt;li&gt;高阶函数：&lt;strong&gt;把算法的变化点变成参数&lt;/strong&gt;（sum/integral 一个模板；map/filter/reduce 是它的日常形态）。&lt;/li&gt;
&lt;li&gt;数据抽象：&lt;strong&gt;用行为约定代替对表示的依赖&lt;/strong&gt;，屏障之下表示随便换。&lt;/li&gt;
&lt;li&gt;闭包性质：&lt;strong&gt;组合产物可再组合&lt;/strong&gt;，小核心才能长成大生态（cons、Unix 管道、JSON 同理）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;四个思维工具，终身受益：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代换模型：纯函数代码的推理方式；引入赋值前先想想值不值得失去它。&lt;/li&gt;
&lt;li&gt;环境模型：变量是“环境里的格子”不是“值”；闭包捕获格子不捕获值——一类经典 bug 的透视镜。&lt;/li&gt;
&lt;li&gt;流/惰性求值：把“随时间变化的状态”重述为“无穷的静态结构”，按需兑现。&lt;/li&gt;
&lt;li&gt;eval/apply：一切解释器、查询引擎、规则引擎、Agent 循环的两行心脏。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;避坑指南：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;读 SICP 不要当“Lisp 教程”读。Scheme 只是手术刀，病灶是“计算的本质”；纠结括号语法就本末倒置了。&lt;/li&gt;
&lt;li&gt;习题一定要做，至少做完第 1、2 章。SICP 的习题（如 1.7 的 &lt;code&gt;good-enough?&lt;/code&gt; 失效分析）是正文的一半。&lt;/li&gt;
&lt;li&gt;不要被第 3 章的状态讨论劝退——代换模型失效、环境模型救场，正是全书承上启下的枢纽，跳过它后面元循环求值器就看不懂。&lt;/li&gt;
&lt;li&gt;元循环求值器别只“看懂”，找一门宿主语言（Python/JS 都行）&lt;strong&gt;亲手写一遍&lt;/strong&gt; eval/apply，哪怕只支持五个特殊形式。写过和看过是两个物种。&lt;/li&gt;
&lt;li&gt;线上资源：MIT 官网有全文免费的 HTML 版（SICP Web Site），1986 年 Abelson 和 Sussman 给惠普员工讲的 20 集录像（MIT OpenCourseWare / &amp;ldquo;SICP Lectures&amp;rdquo;）仍然是最生动的配套。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Harold Abelson, Gerald Jay Sussman, &lt;em&gt;Structure and Interpretation of Computer Programs&lt;/em&gt;, 2nd Edition, MIT Press, 1996（MIT 官网有免费全文）。&lt;/li&gt;
&lt;li&gt;MIT OpenCourseWare：6.001 &lt;em&gt;Structure and Interpretation of Computer Programs&lt;/em&gt;（1986 年 HP 版课程录像）。&lt;/li&gt;
&lt;li&gt;Paul Graham, &lt;em&gt;Hackers &amp;amp; Painters&lt;/em&gt;（Viaweb 与 Lisp 的故事见其中 &amp;ldquo;Beating the Averages&amp;rdquo; 一章）。&lt;/li&gt;
&lt;li&gt;Peter Norvig, &lt;em&gt;Design Patterns in Dynamic Programming&lt;/em&gt;（设计模式与头等函数的关系）。&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
