{"componentChunkName":"component---src-templates-blog-post-js","path":"/the-little-schemer-summary-9/","result":{"data":{"site":{"siteMetadata":{"title":"星雨之夜"}},"markdownRemark":{"id":"7557622f-0e59-5f2d-8f42-9796d3c96b4a","excerpt":"自定义函数 (looking a lat) 定义：在一个 lat 中查找有没有 a，但按照如下规律： 假设 a 是 caviar，lat 是 (6 2 4 caviar 5 7 3)，那么列表的第 1 个元素是 6，第 6 个元素是 7，第 7 个元素是 3，第 3 个元素是 4，第 4 个元素是 caviar…","html":"<h2>自定义函数</h2>\n<h3>(looking a lat)</h3>\n<p>定义：在一个 lat 中查找有没有 a，但按照如下规律：</p>\n<p>假设 a 是 caviar，lat 是 (6 2 4 caviar 5 7 3)，那么列表的第 1 个元素是 6，第 6 个元素是 7，第 7 个元素是 3，第 3 个元素是 4，第 4 个元素是 caviar，它不是数字了，与 a 相同，那么函数结果就是 #t。</p>\n<p>实现：</p>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> looking\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">a</span> lat<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">(</span><span class=\"token function\">keep-looking</span> a <span class=\"token punctuation\">(</span><span class=\"token function\">pick</span> <span class=\"token number\">1</span> lat<span class=\"token punctuation\">)</span> lat<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> keep-looking\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">a</span> sorn lat<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">(</span><span class=\"token keyword\">cond</span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">number?</span> sorn<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">(</span><span class=\"token function\">keep-looking</span> a <span class=\"token punctuation\">(</span><span class=\"token function\">pick</span> sorn lat<span class=\"token punctuation\">)</span> lat<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">(</span><span class=\"token keyword\">else</span> <span class=\"token punctuation\">(</span><span class=\"token function\">eq?</span> sorn a<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>sorn 代表 symbol or number。</p>\n<p>对比之前所学的递归，可以发现这里的 <code class=\"language-text\">keep-looking</code> 并没有对 lat 的某部分进行递归调用。我们把这样的递归称之为“非一般性”递归。那么这样的递归还有什么特点呢？</p>\n<p>继续假设 a 还是 caviar，但 lat 变成 (7 2 4 7 5 6 3)，可以看到查询进程永远不会停下来。我们把 <code class=\"language-text\">looking</code> 这样的函数称为部分函数（Patial Function），以前所接触的那些函数都叫全函数（Total Function）。</p>\n<h3>(eternity x)</h3>\n<p>这里定义了一个最简单的部分函数 <code class=\"language-text\">eternity</code>，它并未完成任何目标，只是传入参数 x，不停递归调用。</p>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> eternity\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">x</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">(</span><span class=\"token function\">eternity</span> x<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3>(shift pair)</h3>\n<p>定义：参数 pair 的第一个元素也是 pair，将这个 pair 的第二个元素移进参数 pair 的第二个元素中，以此构建一个新的 pair。</p>\n<p>实现：</p>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> shift\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">pair</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">(</span><span class=\"token function\">build</span> <span class=\"token punctuation\">(</span><span class=\"token function\">first</span> <span class=\"token punctuation\">(</span><span class=\"token function\">first</span> pair<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n           <span class=\"token punctuation\">(</span><span class=\"token function\">build</span> <span class=\"token punctuation\">(</span><span class=\"token function\">second</span> <span class=\"token punctuation\">(</span><span class=\"token function\">first</span> pair<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n                  <span class=\"token punctuation\">(</span><span class=\"token function\">second</span> pair<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3>(align pora)</h3>\n<p><code class=\"language-text\">align</code> 和 <code class=\"language-text\">keep-looking</code> 一样，在第二个条件中，<code class=\"language-text\">(shift pora)</code> 改变了 <code class=\"language-text\">align</code> 的参数，它不一定是 pora 的一部分，无法保证能够接近最终目标。其实这是违反了第七诫。</p>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> align\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">pora</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">(</span><span class=\"token keyword\">cond</span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token function\">atom?</span> pora<span class=\"token punctuation\">)</span> pora<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token function\">a-pair?</span> <span class=\"token punctuation\">(</span><span class=\"token function\">first</span> pora<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">(</span><span class=\"token function\">align</span> <span class=\"token punctuation\">(</span><span class=\"token function\">shift</span> pora<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">(</span><span class=\"token keyword\">else</span> <span class=\"token punctuation\">(</span><span class=\"token function\">build</span> <span class=\"token punctuation\">(</span><span class=\"token function\">first</span> pora<span class=\"token punctuation\">)</span>\n                   <span class=\"token punctuation\">(</span><span class=\"token function\">align</span> <span class=\"token punctuation\">(</span><span class=\"token function\">second</span> pora<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3>(length* pora)</h3>\n<p>定义：计算 pora 中的原子个数。</p>\n<p>实现：</p>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> length*\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">pora</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">(</span><span class=\"token keyword\">cond</span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token function\">atom?</span> pora<span class=\"token punctuation\">)</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">(</span><span class=\"token keyword\">else</span>\n        <span class=\"token punctuation\">(</span><span class=\"token function\">plus</span> <span class=\"token punctuation\">(</span><span class=\"token function\">length*</span> <span class=\"token punctuation\">(</span><span class=\"token function\">first</span> pora<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n              <span class=\"token punctuation\">(</span><span class=\"token function\">length*</span> <span class=\"token punctuation\">(</span><span class=\"token function\">second</span> pora<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3>(shuffle pora)</h3>\n<p>类似于 <code class=\"language-text\">align</code>，用 <code class=\"language-text\">revpair</code> 替代 <code class=\"language-text\">shift</code>。</p>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> shuffle\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">pora</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">(</span><span class=\"token keyword\">cond</span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token function\">atom?</span> pora<span class=\"token punctuation\">)</span> pora<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token function\">a-pair?</span> <span class=\"token punctuation\">(</span><span class=\"token function\">first</span> pora<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">(</span><span class=\"token function\">shuffle</span> <span class=\"token punctuation\">(</span><span class=\"token function\">revpair</span> pora<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">(</span><span class=\"token keyword\">else</span> <span class=\"token punctuation\">(</span><span class=\"token function\">build</span> <span class=\"token punctuation\">(</span><span class=\"token function\">first</span> pora<span class=\"token punctuation\">)</span>\n                   <span class=\"token punctuation\">(</span><span class=\"token function\">shuffle</span> <span class=\"token punctuation\">(</span><span class=\"token function\">second</span> pora<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>假设 pora 是 ((a b) (c d))，那么 <code class=\"language-text\">(shuffle pora)</code> 递归不会停止，所以它是一个部分函数。</p>\n<h3>(A n m)</h3>\n<p>定义：参考<a href=\"https://en.wikipedia.org/wiki/Ackermann_function\">阿克曼函数</a>。</p>\n<p>实现：</p>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> A\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">n</span> m<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">(</span><span class=\"token keyword\">cond</span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token function\">zero?</span> n<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token function\">add1</span> m<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token function\">zero?</span> m<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token function\">A</span> <span class=\"token punctuation\">(</span><span class=\"token function\">sub1</span> n<span class=\"token punctuation\">)</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">(</span><span class=\"token keyword\">else</span> <span class=\"token punctuation\">(</span><span class=\"token function\">A</span> <span class=\"token punctuation\">(</span><span class=\"token function\">sub1</span> n<span class=\"token punctuation\">)</span>\n               <span class=\"token punctuation\">(</span><span class=\"token function\">A</span> n <span class=\"token punctuation\">(</span><span class=\"token function\">sub1</span> m<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>这是一个全函数，但是不是所有值都能轻易计算出来，比如 <code class=\"language-text\">(A 4 3)</code>。</p>\n<h2>停机问题</h2>\n<p>经历了这么多全函数和部分函数的讨论，我们设计一个函数，用来判断某些函数：针对每个参数，是否返回一个值。形式如下：</p>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> will-stop?\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">f</span><span class=\"token punctuation\">)</span>\n    ...<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>它总是返回 #t 或者 #f，所以是一个全函数。接下来代入几个函数，逐个分析他们是不是会 stop？函数的参数都用空列表来代替，这样最简单。</p>\n<ol>\n<li>\n<p>length</p>\n<p><code class=\"language-text\">(length (quote ()))</code> 是 0，这时 <code class=\"language-text\">will-stop? length</code> 为 #t。</p>\n</li>\n<li>\n<p>eternity</p>\n<p><code class=\"language-text\">(eternity (quote ()))</code> 不会返回值，根本停不下来，这时 <code class=\"language-text\">will-stop? length</code> 显然是 #f。</p>\n</li>\n<li>\n<p>last-try</p>\n<p>定义 <code class=\"language-text\">last-try</code>：</p>\n<div class=\"gatsby-highlight\" data-language=\"scheme\"><pre class=\"language-scheme\"><code class=\"language-scheme\"><span class=\"token punctuation\">(</span><span class=\"token keyword\">define</span> last-try\n  <span class=\"token punctuation\">(</span><span class=\"token keyword\">lambda</span> <span class=\"token punctuation\">(</span><span class=\"token function\">x</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">(</span><span class=\"token function\">and</span> <span class=\"token punctuation\">(</span><span class=\"token function\">will-stop?</span> last-try<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">(</span><span class=\"token function\">eternity</span> x<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p><code class=\"language-text\">(last-try (quote ()))</code> 的值也就是 <code class=\"language-text\">(and (will-stop? last-try) (eternity (quote ())))</code> 的值。考虑以下两种情况：</p>\n<ul>\n<li>\n<p><code class=\"language-text\">(will-stop? last-try)</code> 是 #f，表示 <code class=\"language-text\">last-try</code> 停不下来，则：</p>\n<ol>\n<li><code class=\"language-text\">(and (will-stop? last-try) (eternity (quote ())))</code> = #f；</li>\n<li><code class=\"language-text\">(last-try (quote ()))</code> = #f，表示 <code class=\"language-text\">last-try</code> 能够停下来。嗯？矛盾。</li>\n</ol>\n</li>\n<li>\n<p><code class=\"language-text\">(will-stop? last-try)</code> 是 #t，表示 <code class=\"language-text\">last-try</code> 能够停下来，则：</p>\n<ol>\n<li><code class=\"language-text\">(and (will-stop? last-try) (eternity (quote ())))</code>，后面的 <code class=\"language-text\">eternity</code> 停不下来，整体也停不下来啊；</li>\n<li>于是 <code class=\"language-text\">(last-try (quote ()))</code> 也停不下来。嗯？？？也矛盾了。</li>\n</ol>\n</li>\n</ul>\n</li>\n</ol>\n<p>所以对于 <code class=\"language-text\">will-stop?</code>，如果我们能够定义它，那么它必然会生成 #t 或 #f；但是上面的例子告诉我们有时这是做不到的。要定义清楚 <code class=\"language-text\">will-stop?</code> 就必须意味着它无法被定义。说人话就是我们写不出 <code class=\"language-text\">will-stop?</code> 这样的函数。详见<a href=\"https://en.wikipedia.org/wiki/Halting_problem\">停机问题</a>。</p>\n<h2>Y 组合子</h2>\n<p>本章的余下内容在构造 Y 组合子，即大名鼎鼎的 Y Combinator。我看了好几遍，查了一堆资料，才懂了个大概，感觉值得单独写一篇文章来详细叙述，把「The Little Schemer」第九章的论述方法用自己的理解再写一遍。</p>","frontmatter":{"title":"The Little Schemer 知识点整理（九）：……周而复始……","date":"2019 年 01 月 24 日","description":"这一章介绍了“非一般性”递归，这样的递归可能会使函数进入无限循环或者引发悖论，比如“停机问题”。然后讨论了在函数无法被定义（名字）的情况下如何实现递归，即 Y 组合子。"}}},"pageContext":{"slug":"/the-little-schemer-summary-9/","previous":{"fields":{"slug":"/the-little-schemer-summary-8/"},"frontmatter":{"title":"The Little Schemer 知识点整理（八）：Lambda 终结者"}},"next":{"fields":{"slug":"/recursive-to-y-combinator/"},"frontmatter":{"title":"从递归到 Y 组合子"}}}}}