这个函数作者的原意是删除表中test位真的部分,并且表按原样返回.

作者给出的的测试用例如下:

(prune #'evenp '(1 2 (3 (4 5) 6) 7 8 (9)))

返回结果是:

(1 (3 (5)) 7 (9))

这里的(9)应为刚好被evenp判断为假,所以正常包含在列表当中了,可是当有类似(9)这样的单元素列表包含在内的特殊情况就被作者忽略了,我偶然输入了如下测试用例,起初只是为了观察嵌套列表的层数是否因递归而减少.

(prune #'evenp '((((1 2) 3 4)) 5 (6) (7) (((8 9)))))

得到如下的结果:

((((1) 3)) 5 NIL (7) (((9))))

嵌套层数未变,可是却多出了一个nil,仔细观察代码,会发现这个nil必然是多余的,如果不是多余的,这个函数可以用很容易理解的递归完成.于是下面就想着自己完善这个实用工具.

(defun prune (test tree)
(labels ((rec (tree acc)
(cond ((null tree) (nreverse acc)) ;4
((consp (car tree))
(rec (cdr tree)
(cons (rec (car tree) nil) acc))) ;2
(t (rec (cdr tree)
(if (funcall test (car tree))
acc ;3
(cons (car tree) acc)))))))
(rec tree nil))) ;1

首先,肯定是多了个nil元素被添加到列表中,所以我们要寻找这个nil的来源.

先看4,这个是递归函数出口,nil完全不可能来自这里,而1是入口,这里的nil明显是迭代列表.

那剩下来只有2和3了.仔细观察,发现进入t分支的时,只要是由于分支2的调用,acc便是nil,只有在3分支的下一个元素是在一个层级上,才能迭代这个层级的表项.也就是说,真正产生表项的,是分支3.既然找到了nil的源头,那问题就简单了,只要分支3返回nil,就忽略掉.

(defun prune (test tree)
(labels ((rec (tree acc)
(cond ((null tree) (nreverse acc))
((consp (car tree))
(let ((ret (rec (car tree) nil))) ;fixed
(if ret
(rec (cdr tree) (cons ret acc))
(rec (cdr tree) acc))))
(t (rec (cdr tree)
(if (funcall test (car tree))
acc
(cons (car tree) acc)))))))
(rec tree nil)))

《On Lisp》第四章第三节图4.3中的prune函数fix的更多相关文章

  1. 《On Lisp》第四章第三节图4.6中的rmapcar函数中展现的apply陷阱

    (defun rmapcar (fn &rest args) (if (some #'atom args) (apply fn args) (apply #'mapcar #'(lambda ...

  2. 微信小程序教学第四章第三节(含视频):小程序中级实战教程:详情-功能完善

    详情 - 功能完善 本文配套视频地址: https://v.qq.com/x/page/f0555nfdi14.html 开始前请把 ch4-3 分支中的 code/ 目录导入微信开发工具 这一节中, ...

  3. jQuery_第四章_思维图

    ---------------------------------------------------------------------------------------------------- ...

  4. 啊哈算法第四章第三节 层层递进-广度优先搜索 java实现

    package corejava; public class FourThree { static int [][]a=new int[50][50]; static int [][]b=new in ...

  5. 剑指offer-第四章解决面试题的思路(包含min函数的栈)

    题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数,在该栈中,调用min,push及pop的时间复杂度都是O(1) 思路:定义两个栈分别为dataStack和minStack ...

  6. C# Language Specification 5.0 (翻译)第四章 类型

    C# 语言的类型分为两大类:值类型(value type)和引用类型(reference type),而它们又都同时具有至少一个类型形参的泛型类型(generic type).类型形参(type pa ...

  7. 第四章:重构代码[学习Android Studio汉化教程]

    第四章 Refactoring Code The solutions you develop in Android Studio will not always follow a straight p ...

  8. 【软件构造】第三章第三节 抽象数据型(ADT)

    第三章第三节 抽象数据型(ADT) 3-1节研究了“数据类型”及其特性 ; 3-2节研究了方法和操作的“规约”及其特性:在本节中,我们将数据和操作复合起来,构成ADT,学习ADT的核心特征,以及如何设 ...

  9. 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...

随机推荐

  1. 【学】React的学习之旅1

    React的学习之旅1 单标签要有斜杠代表结束 用React.createClass()方法时,赋值后的组件名称首字母一定要大写 一定要先定义组件,再用ReactDOM.render调用 组件里ren ...

  2. Oracle补习班第三天

    In every triumph, there's a lot of try. 每个胜利背后都有许多尝试 Oracle管理实例组件 主要组件分为两部分例程,与数据库: 例程分为两部分SGA跟进程: S ...

  3. JSTL 核心标签库 使用

    JSTL 核心标签库标签共有13个,功能上分为4类: 1.表达式控制标签:out.set.remove.catch 2.流程控制标签:if.choose.when.otherwise 3.循环标签:f ...

  4. Windows平台分布式网站系统应用(转)

    概述 最近.NET的世界开始闹腾了,微软官方终于加入到了对.NET跨平台的支持,并且在不久的将来,我们在VS里面写的代码可能就可以通过Mono直接在Linux和Mac上运行.那么大家(开发者和企业)为 ...

  5. linux默认编辑器 sublime

    开发人员把Vi improved简称Vim Vim编辑器再内存缓冲区中处理数据. 如果再启动Vim时未指定文件名,或者这个文件不存在,Vim会新开一段缓冲区域来编辑. 如果你再命令行下指定了一个已有文 ...

  6. d3安装异常

    使用npm安装D3,发现其工程名和依赖名重复,导致安装异常 http://thisdavej.com/node-newbie-error-npm-refusing-to-install-package ...

  7. Codeforces 730I [费用流]

    /* 不要低头,不要放弃,不要气馁,不要慌张 题意: 给两行n个数,要求从第一行选取a个数,第二行选取b个数使得这些数加起来和最大. 限制条件是第一行选取了某个数的条件下,第二行不能选取对应位置的数. ...

  8. 普通B/S架构模式同步请求与AJAX异步请求区别(个人理解)

    在上次面试的时候有被问到过AJAX同步与异步之间的概念问题,之前没有涉及到异步与同步的知识,所以特意脑补了一下,不是很全面... 同步请求流程:提交请求(POST/GET表单相似的提交操作)---服务 ...

  9. BAE log服务的配置(nodejs)

    log4js.loadAppender('baev3-log'); var options = { 'user': appConfig.AK, //这里是在用户的安全认证中的Access Key ID ...

  10. 安装了多个Oracle11g的客户端,哪个客户端的tnsnames.ora会起作用?

    如果我们由于需要安装了多个Oracle的client,哪个客户端的tnsnames.ora会起作用呢? 答案是: 在安装好clinent端后,安装程序会把client的bin目录放到path里面,pa ...