2020-03-08lisp学习-7

42/ 理解递归 (Understanding Recursion)

递归的优点是它精确地让我们更抽象地来设计算法。要知道一个递归函数是否做它该做的事,你只需要问,它包含了所有的情况吗?举例来说,下面是一个寻找列表长度的递归函数:

一个列表只有空或者非空两种情况,递归函数编写时涵盖这两种情况,每种情况都有对应的输出值,可认为函数编写正确。

;; * (defun len (lst);;;定义函数名及形参
    (if (null lst)  ;;判断是否空,是真,否假。考虑列表为空的情况
        0;;真,列表为空时返回值
        (+ (len (cdr lst)) 1)));;;假,列表不为空时返回值。1为car的长度,cdr为剩余列表的长度。
LEN
* (len '(1 2 3));;;;;3

更复杂的递归函数,可能会有更多的情况需要讨论,但是流程是一样的。举例来说, 41 页的our-copy-tree,我们需要讨论三个情况:

原子,'3;;;单原子,不可再分

单一的Cons对象,'(1 2 3);;cons中只有元素

n+1的Cons树。'('(5 4) 1 2 3);;;cons中含有列表

已知一个值与一个列表,返回列表中第一个以已知值为首值和后续元素组成的新列表。

* (defun our-member (obj lst);;;定义函数名及形参
  (if (eql (car lst) obj);;;判断列表首值与已知值是否相同,是真,否假
      lst;;是,输出列表
      (our-member obj (cdr lst))));;;否,继续去列表后续部分循环。由于未设定空列表情况,此循环会无尽循环下去,cdr nil仍未nil

我们需要初始一个null测试,确保在到达列表底部时,没有找到目标时要停止递归。如果我们要找的对象没有在列表里,这个版本的member会陷入无穷循环。

* (defun our-member (obj lst)
    (if (eql (car lst) obj)
       lst
       (if (null lst);;;判断列表是否为空,是真,否假
           nil ;;真,返回nil,结束命令。
           (our-member obj (cdr lst)) )));;假,继续循环,直到列表中所有元素都与obj比较了一遍,最后返回空列表

43/ 键字参数:test参数。

例如,如果你在调用member时,传入某个函数作为:test参数,那么那个函数就会被用来比较是否相等,而不是用eql。

所以如果我们想找到一个给定的对象与列表中的成员是否相等(equal),我们可以:

* (member '(a) '((a) (z)) :test #'equal);;比较(a)是否与列表中的某个元素相等(即列表中是否含有该元素),是,返回列表;否,nil
((A) (Z));;;结果,返回列表

*  (member '(b) '((a) (z)) :test #'equal)

NIL;;;不包含,返回nil

43/ 键字参数:key参数。

* (member 'c '((a b) (c d)) :key #'car);;;;;((C D));;;判断列表中所有成员的首值是否与已知对象相等,若相等则返回该成员为首值的新列表。否则返回nil

* (member 'a '((a b) (c d)) :key #'car)

((A B) (C D))

* (member 'e '((a b) (c d)) :key #'car)

NIL

* (member 'c '((a b) (c d)) :key #'car)

((C D))

组合使用关键字参数,这两个调用是等价的:

* (member 2 '((1) (2)) :key #'car :test #'equal);;;;((2))

* (member 2 '((1) (2)) :test #'equal :key #'car);;;;((2))

* (member 1 '((1) (2)) :key #'car :test #'equal)

((1) (2))

44/ oddp 判断一个数是否为奇数

* (oddp 4);;;NIL
*  (oddp 5);;T

45/  member-if,判断一个列表经某个函数执行后,返回函数体所要表达的列表值,否则nil

* (member-if #'oddp '(2 3 4));;;;(3 4);;;;;返回列表奇数为首值的新列表。
* (member-if #'oddp '(2 6 4));;;;NIL;;;列表无奇数。

以下为member-if 的递归表达式

* (defun our-member-if (fn lst);;;定义函数名及形参,fn代表一个函数(灵活设定)
  (and (consp lst);;;;函数返回值得必要条件,lst为列表;否则nil,程序结束
       (if (funcall fn (car lst));;;判断lst首值执行函数后是否为真
           lst;;真,返回表
           (our-member-if fn (cdr lst)))));;;假,cdr值组成新lst继续循环。

46/ 函数adjoin像是条件式的cons。它接受一个对象及一个列表,如果对象还不是列表的成员,才构造对象至列表上。通常的情况下它接受与 member 函数同样的关键字参数。

* (adjoin 'b '(a b c));;;;(A B C)
* (adjoin 'z '(a b c));;;;(Z A B C)

47/ 并集 (union)、交集 (intersection)以及补集 (complement)的实现,是由函数union、intersection以及set-difference。

这些函数期望两个(正好 2 个)列表(一样接受与member函数同样的关键字参数)。

* (union '(a c B) '(c b s));;;(A C B S)
* (union '(a b c E R) '(c b s));;;(R E A C B S);;;合并表,剔除第一个表中的相同元素,并把第一个表中未重复元素由后向前排序,然后再排序第二个表全部。

* (intersection '(a b e c) '(d c b e));;;(C E B);;;;求两表相同值组成新lst,以第一个表格元素顺序为准,倒序排列新lst

* (set-difference '(a e c d b) '(b e f));;;;(D C A);两表差集。一表减去二表中相同元素,并对一表剩余元素倒序排列。

因为集合中没有顺序的概念,这些函数不需要保留原本元素在列表被找到的顺序。举例来说,调用set-difference也有可能返回(A C D)。

48/ length返回序列中元素的数目。

* (length '(a b c));;;;;3

49/ subseq,复制序列的一部分。

(subseq LST x y);;x从列表的哪个位置复制元素(0为第一个位置),y为复制几个元素

* (subseq '(a b c d) 0 1);;(A)

*  (subseq '(a b c d) 0 2);;(A B)

*  (subseq '(a b c d) 0 3);;(A B C)

*  (subseq '(a b c d) 0 4);;(A B C D)

* (subseq '(a b c d) 1 2);;;;(B)
* (subseq '(a b c d) 1 3);;;;(B C)
* (subseq '(a b c d) 1 4);;;;(B C D)

50/ reverse返回与其参数相同元素的一个序列,但顺序颠倒。

>(reverse '(a b c));;;;(C B A)

51/ 如果一个回文有偶数个元素,那么判断后半段会是前半段的镜射 (mirror)?。是t否nil

* (defun mirror? (s);;;函数名及形参
  (let ((len (length s)));;;定义变量并赋值(列表的长度数值)
    (and (evenp len);;;必要条件,判断是否为偶数,否nil
         (let ((mid (/ len 2)));;;定义变量并赋值(列表的长度数值的一半)
           (equal (subseq s 0 mid);;;比较两元素,是t,否nil;其一为复制s列表前半
                  (reverse (subseq s mid)))))));;;其一为复制s列表后半

MIRROR?判断一个列表中的元素是否对称分布
*  (mirror? '(a b b a));;;T

52/ sort,内置的排序函数,接受一个序列及一个比较两个参数的函数,返回一个有同样元素的序列,根据比较函数来排序:

*  (sort '(0 2 1 3 8) #'>);;;(8 3 2 1 0)
*  (sort '(0 2 1 3 8) #'<);;;(0 1 2 3 8)


使用sort及nth,我们可以写一个函数,接受一个整数n,返回列表中第n大的元素:


* (defun nthmost (n lst);;;函数名及形参
  (nth (- n 1);;;返回列表中第n-1位置的数值(起始位为0)

      (sort (copy-list lst) #'>)));;;复制新列表并按从大到小排序
;;;;NTHMOST

* (nthmost 2 '(0 2 1 3 8));;;3

53/ 函数every和some接受一个判断式及一个或多个序列。当我们仅输入一个序列时,它们测试序列元素是否满足判断式:

* (every #'oddp '(1 3 5));;;T;;判断列表元素是否都为奇数
* (some #'evenp '(1 2 3));;;T;;判断列表元素是否包含偶数
* (some #'evenp '(1 5 3));;;NIL;;判断列表元素是否包含偶数
* (every #'oddp '(1 2 3));;;;NIL;;判断列表元素是否都为奇数

* (every #'> '(1 3 5) '(0 2 4));;;T
* (every #'> '(1 3 5) '(1 2 4));;;NIL

如果它们输入多于一个序列时,判断式必须接受与(最短)序列一样多的元素作为参数,而参数从所有序列中一次提取一个元素:

* (every #'> '(1 3 5 6) '(0 1 4 4 7 8));;T

*  (every #'> '(1 3 5 6) '(0 1 4 4 7 8) '(0 0 0));;;NIL;;对应位置元素比较大小
*  (every #'> '(3 3 5 6) '(2 1 4 4 7 8) '(1 0 2));;;T如果序列有不同的长度,最短的那个序列,决定需要测试的次数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352