列表

1. 构造(Conses)


cons真正所做的事情是把两个对象结合成一个具有两部分的对象,称为cons对象
概念上来说是一对指针,carcdr

2. 等式(Equality)


每一次调用cons时,Lisp会配置一块新的内存给两个指针,所以使用通常参数调用cons两次,会产生出两个数值一样的两个对象。

  • eql 当两个对象是同一个时才返回真
    -equal两个参数的值相同时返回真

3.为什么Lisp没有指针


  • 每个值在概念上来说都是一个指针,只不过Lisp不会像C语言那样显示操作指针。
  • 程序员可以把任何东西放在任何地方,可以在任何数据结构存放任何类型的对象,包括结构本身。

4. 建立列表(Buiding Lists)


  • copy-list接受一个列表,并返回列表的复本。
  • append返回任何数目列表的串接(concatenation)

5. 存取(Access)


列表中的元素是从0开始编号的

  • nth找到列表特定位置的元素
    > (nth 0 '(a b c))
    A
    
  • nthcdr找到第n个cdr
    > (nthcdr 2 '(a b c))
    (c)
    
  • zerop仅在参数为0时才返回真, nil会报错
  • last返回列表的最后一个cons对象,这与取得最后一个元素不同。
    如果你要取得列表的最后一个元素你需要取得lastcar
  • Common Lisp第一了从first到tenth这些可以取得列表对应元素的函数。
    这些函数不是零索引(zero-indexed)
  • cxrCommon Lisp定义了类似caddr这样的函数,表示几个cdr的cdr的car的缩写。
    最多四个a或d,不过这样并不易读,所以不推荐。

7. 映射函数(Mapping Functions)


映射函数就是对一个列表中的所有元素做函数调用。

  • mapcar接受一个函数及一个或多个列表,并把函数应用至每个列表的元素中。
    > (mapcar #'list
                     '(a b c)
                     '(1 2 3 4))
    ((A 1) (B 2) (C 3))
    
  • maplist接受一个函数及一个或多个列表,并将列表cdr后的列表传入函数。
    > (maplist #'(lambda (x) x)
                         '(a b c))
    ((A B C) (B C) (C))
    

8. 树(Trees)


cons对象可以想象成二叉树,car代表左子树,cdr代表右子树。
操作树的函数通常使用carcdr同时做递归,这种函数被称为双重递归(diubly recursive)

9. 理解递归(Understanding Recursion)


检查两件事来判断递归函数是正确的:

  • 对长度为0的列表是有效的。
  • 给定它对于长度为n的列表是有效的,它对长度是n+1的列表也是有效的。

10. 集合(Sets)


列表是表示小集合的好方法
集合中没有顺序的概念,所以不需要保留元素元素的顺序

  • member接受一个查找的元素和一个被查找的列表,返回找到的元素及之后的元素。
    可以接受关键字参数:test:key
  • member-if接受一个函数及一个列表,从第一个被应用函数后返回真的元素位置返回。
  • adjoin接受一个元素和一个列表,如果该元素还不是列表成员,则将其加入列表。
  • union生成并集的函数
  • intersection生成交集的函数
  • set-difference生成补集的函数

11. 序列(Sequences)


一系列有特定顺序的列表。

  • subseq接受三个参数,第一个为列表,第二个为开始截取的位置,第三个为结束的位置,不过为可选的,如果没有第三个参数,则直接截取到列表结尾。
  • reverse返回与参数相同的列表,但顺序不同。
  • sort对传入的列表进行排序,是破坏性的(destructive)即会破坏掉原有的顺序,只可以排序数字。
    (sort '(0 2 1 3 8) #'>)
    
  • every接受一个判断函数及一个或多个序列(取决于判断函数接受几个参数),所有序列的元素判断为真,则为真
    > (every #'oddp '(1 3 5))
    T
    
  • some接受一个判断函数及一个或多个序列(取决于判断函数接受几个参数),有一个元素判断为真则为真
    > (some #'evenp '(1 2 3))
    T
    

12. 栈(Stacks)


  • push放入列表前段
  • pop将列表的第一个元素移除,并返回这个元素
  • pushnew宏push的变种,使用了adjoin而不是cons

13. 点状列表(Dotted Lists)


用点(.)来分割开的列表,称为点状列表。点状列表不是一个正规列表。
点状列表是一个成对的Cons对象,而正规列表不是。
点状列表可以作为类似映射表的方式使用,因为是成对的,而不像正规列表是连接的。

> (setf pair (cons 'a 'b))
(A . B)

14. 关联列表(Assoc-lists)


一个由cons对象组成的列表称之为关联列表(assoc-listor alist)。关联列表很慢,但在初期的程序中使用很方便。

> (setf trans '((+ . "add")))
> (assoc '+ trans)
(+ . "add")

15. 垃圾(Garbages)


列表提供顺序存取而不是随机存取,所以会比数组慢。
Cons对象倾向于用指针表示,所以访问一个列表意味着访问一系列的指针。
虽然这样比简单地像数组一样增加索引值慢,但所话费的代价与配置及回收Cons单元比起来小多了。

垃圾回收可以让程序员不比现实的使用allocate及dellocate进行内存管理,而是由Lisp进行内存管理,这样有效的避免内存泄漏及悬垂指针的问题。

但垃圾回收也是需要代价的。所以如果需要写出高性能的Lisp程序是需要时时考虑对Cons对象的使用,因为Cons对象始终是需要被回收的。

有些Lisp实现回收的Cons代价很大,而有的Lisp实现,内存管理相当好,以至于使用cons比不适用cons更快。

习题(Exercises)


  • (a) (cons 'a (cons 'b (cons (cons 'c (cons 'd nil)) nil)))
  • (b)(cons 'a (cons (cons 'b (cons (cons 'c (cons (cons 'd nil) nil)) nil)) nil))
  • (c)(cons (cons (cons 'a (cons 'b nil)) (cons 'c nil)) (cons 'd nil))
  • (d)(cons 'a (cons (cons 'b 'c) (cons 'd nil)))
  (defun new-union (lst1 lst2)
    (if (null lst2)
      lst1
      (if (null (member (car lst2) lst1))
        (append (new-union lst1 (cdr lst2))
              (cons (car lst2) nil))
        (new-union lst1 (cdr lst2)))))
  (defun occurrences (ls)
    (let ((acc nil))
      (dolist (obj ls)
        (let ((p (assoc obj acc)))
          (if p
            (incf (cdr p))
            (push (cons obj 1) acc))))
      (sort acc #'> :key #'cdr)))
  1. 因为member使用eql来比较对象

  • (a)递归
      (defun rec-pos+ (lst)
         (if (null lst)
              lst
              (let* ((lst-pos (- (length lst) 1))
                          (cur-ele (nth lst-pos lst))
                          (new-lst '()))
                    (append new-lst
                                    (rec-pos+ (remove cur-ele
                                                                        lst))
                                     (cons (+ cur-ele lst-pos) nil)))))
    
  • (b)迭代
      (defun ite-pos+ (lst)
        (if (null lst)
          lst
          (let ((lst-len (length lst))
                   (new-lst '()))
                (progn
                  (do ((i 0 (+ i 1)))
                    ((>= i lst-len) new-lst)
                      (setf new-lst
                              (append new-lst
                                              (cons (+ (nth i lst)
                                                               i)
                                                          nil))))))))
    
  • (c)mapcar
      (defun map-pos+ (lst)
          (if (null lst)
                lst
                (let ((cur-pos 0))
                  (mapcar #'(lambda (x)
                                        (progn
                                          (setf x (+ x cur-pos))
                                          (setf cur-pos (+ cur-pos 1))
                                          x))
                                  lst))))
    
  • (a)cons
  (defun our-cons (x y)
    (let ((ls '(nil .nil)))
      (setf (car ls) x
          (cdr ls) y)
      ls))
  • (b)list
   (defun our-list (&rest items)
       (our-list-0 items))

   (defun our-list-0 (lst)
       (if lst
           (cons (car lst)
             (our-list-0 (cdr lst)))))
  • (c)length (for lists)
(defun our-length (lst)
  (if lst
      (+ 1 
         (our-length (cdr lst)))
    0))
  • (d)member (for lists; no keywords)
(defun our-member (target lst)
  (if lst
      (if (eq target (car lst))
          lst
        (our-member target (cdr lst)))))
  (defun n-elts (elt n)
      (if (> n 1)
            (cons n elt) ;; 将list修改为cons,因为list本身会使用多个cons
      elt))
  (defun showdots (lst)
      (showdots-rec lst 0))

  (defun showdots-rec (lst n)
  (if lst
      (progn
        (if (atom (car lst))
            (format t "(~A . " (car lst))
          (progn
            (format t "(")
            (showdots-rec (car lst) 0)
            (format t " . ")))
        (showdots-rec (cdr lst) (+ 1 n)))
    (progn
      (format t "nil")
      (dotimes (j n)
        (format t ")")))))

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

推荐阅读更多精彩内容