`
iyuan
  • 浏览: 469897 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

lisp初体验-Practical Common Lisp笔记-13.集合

    博客分类:
  • lisp
阅读更多

上一章有介绍到Lisp的基本原子态的数据格式,本章由题可知,讲的是由原子构建成的分子形态。

 

集合这个概念在现代计算机语言中有着广泛的应用(有不用的么?),比较常见的有数组、元组、列表、哈希表、字典等等。 而在Lisp中最为常用也最为人所知的估计就是“列表”了,list,lisp,还的确蛮像的~不知是这个原因还是其他,相当一部分的Lisp的书籍中干脆只见list不见其他了。而事实上,作为"智能化"语言,自然是不可能只有这么一种集合形式,故而本章顶着"集合"之名向大家伙介绍了Lisp中其他的一些集合形态:向量和哈希表。

 

向量算是Lisp的一个基本的集合了,以整数作索引,又分为固定向量(大小设定既不变)和可变向量(增删改查,爱咋咋的)两种。

创建向量很简单,使用VECTOR函数:

 

(vector)     ==> #()
(vector 1)   ==> #(1)
(vector 1 2) ==> #(1 2)

 

 不过通常更常用的构建向量函数是MAKE-ARRAY

 

(make-array 5 :initial-element nil) ==> #(NIL NIL NIL NIL NIL)

 

 make-array常用的一个点在于,它构建的向量是可变的,通常的用法是:

 

(defparameter *x* (make-array 5 :fill-pointer 0))

(vector-push 'a *x*) ==> 0
*x*                  ==> #(A)
(vector-push 'b *x*) ==> 1
*x*                  ==> #(A B)
(vector-push 'c *x*) ==> 2
*x*                  ==> #(A B C)
(vector-pop *x*)     ==> C
*x*                  ==> #(A B)
(vector-pop *x*)     ==> B
*x*                  ==> #(A)
(vector-pop *x*)     ==> A
*x*                  ==> #()

 

 通常来说如果搭配上:adjustable参数就使得向量可变:

 

(make-array 5 :fill-pointer 0 :adjustable t) ==> #()

 

 当需要增大向量规模时,用VECTOR-PUSH-EXTEND,用法和vector-push一样(话说我用的sbcl,即使不用上面的

神奇参数也是可以扩展的..)

在一些深入业务的操作中,有时也会对数据格式有要求,那么:element-type参数就有了用武之地

 

(make-array 5 :fill-pointer 0 :adjustable t :element-type 'character)

 

 如此构建的向量应该算是特殊向量吧。

向量作为序列的一个子集,也包含了序列的很多特性(这些特性同样适用于其他类型的序列包括列表)

序列最基础的两个函数:LENGTHELT:

 

(defparameter *x* (vector 1 2 3))

(length *x*) ==> 3
(elt *x* 0)  ==> 1
(elt *x* 1)  ==> 2
(elt *x* 2)  ==> 3
(elt *x* 3)  ==> error

 

 elt通常还会和setf合用:

 

(setf (elt *x* 0) 10)
*x* ==> #(10 2 3)

 

 上述的基础函数可以组合出n多花样,不过为了方便,lisp已经提供了一些常用、靠谱的函数用以迭代、遍历序列:

 

Name Required Arguments Returns
COUNT Item and sequence Number of times item appears in sequence
FIND Item and sequence Item or NIL
POSITION Item and sequence Index into sequence or NIL
REMOVE Item and sequence Sequence with instances of item removed
SUBSTITUTE New item, item, and sequence Sequence with instances of item replaced with new item

 

 

 

 

 

 

 

 

 

 

 

使用方式:

 

(count 1 #(1 2 1 2 3 1 2 3 4))         ==> 3
(remove 1 #(1 2 1 2 3 1 2 3 4))        ==> #(2 2 3 2 3 4)
(remove 1 '(1 2 1 2 3 1 2 3 4))        ==> (2 2 3 2 3 4)
(remove #\a "foobarbaz")               ==> "foobrbz"
(substitute 10 1 #(1 2 1 2 3 1 2 3 4)) ==> #(10 2 10 2 3 10 2 3 4)
(substitute 10 1 '(1 2 1 2 3 1 2 3 4)) ==> (10 2 10 2 3 10 2 3 4)
(substitute #\x #\b "foobarbaz")       ==> "fooxarxaz"
(find 1 #(1 2 1 2 3 1 2 3 4))          ==> 1
(find 10 #(1 2 1 2 3 1 2 3 4))         ==> NIL
(position 1 #(1 2 1 2 3 1 2 3 4))      ==> 0

 

 当然,上面只是标准的使用方式,鉴于大伙的不走寻常路,这些函数都支持一些通用的参数:

 

(count "foo" #("foo" "bar" "baz") :test #'string=)    ==> 1
(find 'c #((a 10) (b 20) (c 30) (d 40)) :key #'first) ==> (C 30)

(find 'a #((a 10) (b 20) (a 30) (b 40)) :key #'first)             ==> (A 10)
(find 'a #((a 10) (b 20) (a 30) (b 40)) :key #'first :from-end t) ==> (A 30)

(remove #\a "foobarbaz" :count 1)             ==> "foobrbaz"
(remove #\a "foobarbaz" :count 1 :from-end t) ==> "foobarbz"

 像:from-end这种参数对count函数似乎意义不大,不过,对于倒序什么还是有用处的:

 

CL-USER> (defparameter *v* #((a 10) (b 20) (a 30) (b 40)))
*V*
CL-USER> (defun verbose-first (x) (format t "Looking at ~s~%" x) (first x))
VERBOSE-FIRST
CL-USER> (count 'a *v* :key #'verbose-first)
Looking at (A 10)
Looking at (B 20)
Looking at (A 30)
Looking at (B 40)
2
CL-USER> (count 'a *v* :key #'verbose-first :from-end t)
Looking at (B 40)
Looking at (A 30)
Looking at (B 20)
Looking at (A 10)
2

 估计这么一堆代码下来,有些筒子要受不鸟,那么整理下,看张表格:

 

Argument     Default     Meaning  
:test EQL Two-argument function used to compare item (or value extracted by :key function) to element.
:key NIL One-argument function to extract key value from actual sequence element. NIL means use element as is.
:start 0 Starting index (inclusive) of subsequence.
:end NIL Ending index (exclusive) of subsequence. NIL indicates end of sequence.
:from-end NIL If true, the sequence will be traversed in reverse order, from end to start.
:count NIL Number indicating the number of elements to remove or substitute or NIL to indicate all (REMOVE and SUBSTITUTEonly).

 

舒活下筋骨,咱对前面的方法做些许改造:

 

(count-if #'evenp #(1 2 3 4 5))         ==> 2

(count-if-not #'evenp #(1 2 3 4 5))     ==> 3

(position-if #'digit-char-p "abcd0001") ==> 4

(remove-if-not #'(lambda (x) (char= (elt x 0) #\f))
  #("foo" "bar" "baz" "foom")) ==> #("foo" "foom")

(count-if #'evenp #((1 a) (2 b) (3 c) (4 d) (5 e)) :key #'first)     ==> 2

(count-if-not #'evenp #((1 a) (2 b) (3 c) (4 d) (5 e)) :key #'first) ==> 3

(remove-if-not #'alpha-char-p
  #("foo" "bar" "1baz") :key #'(lambda (x) (elt x 0))) ==> #("foo" "bar")

(remove-duplicates #(1 2 1 2 3 1 2 3 4)) ==> #(1 2 3 4)

 是不是似曾相识?(不然你就是舒活太久筋骨了~)加了些类似自然英语的后缀,就变成复合函数了。

这里原作者啰嗦了一堆,大家学编程的,都是聪明人,看这代码,哪还不能理解这个~

 

以上根本其实是对序列中的单数据操作,Lisp还有一些可以对整个序列操作的函数:

COPY-SEQ 拷贝完整序列

REVERSE  反向序列

CONCATENATE  连接多序列 (这个需要指明合成序列是什么集合)

 

(concatenate 'vector #(1 2 3) '(4 5 6))    ==> #(1 2 3 4 5 6)
(concatenate 'list #(1 2 3) '(4 5 6))      ==> (1 2 3 4 5 6)
(concatenate 'string "abc" '(#\d #\e #\f)) ==> "abcdef"

 

作为序列,如果连排序都做不到,实在是太说不过去了,lisp提供sort,stable-sort(靠谱加强版?):

 

(sort (vector "foo" "bar" "baz") #'string<) ==> #("bar" "baz" "foo")

 这种排序是会影响到原数据的,故而又称为破坏性函数,虽然如此,仍然建议大家这么写:

 

(setf my-sequence (sort my-sequence #'string<))

 而不是这样:

 

(sort my-sequence #'string<)

 merge函数:类似concatenate,不过它支持排序哦:

 

(merge 'vector #(1 3 5) #(2 4 6) #'<) ==> #(1 2 3 4 5 6)
(merge 'list #(1 3 5) #(2 4 6) #'<)   ==> (1 2 3 4 5 6)

 还有一种需求(需求无限多啊)是要操纵序列的子序列,subseq函数算是代表:

 

(subseq "foobarbaz" 3)   ==> "barbaz"
(subseq "foobarbaz" 3 6) ==> "bar"

 在配合着setf:

 

(defparameter *x* (copy-seq "foobarbaz"))

(setf (subseq *x* 3 6) "xxx")  ; subsequence and new value are same length
*x* ==> "fooxxxbaz"

(setf (subseq *x* 3 6) "abcd") ; new value too long, extra character ignored.
*x* ==> "fooabcbaz"

(setf (subseq *x* 3 6) "xx")   ; new value too short, only two characters changed
*x* ==> "fooxxcbaz"

 相对应的,查找匹配的子序列位置也很方便:

 

(position #\b "foobarbaz") ==> 3
(search "bar" "foobarbaz") ==> 3
(mismatch "foobarbaz" "foom") ==> 3
(mismatch "foobar" "bar" :from-end t) ==> 3

 下面再来介绍下用于序列的判断函数:

 

(every #'evenp #(1 2 3 4 5))    ==> NIL
(some #'evenp #(1 2 3 4 5))     ==> T
(notany #'evenp #(1 2 3 4 5))   ==> NIL
(notevery #'evenp #(1 2 3 4 5)) ==> T

(every #'> #(1 2 3 4) #(5 4 3 2))    ==> NIL
(some #'> #(1 2 3 4) #(5 4 3 2))     ==> T
(notany #'> #(1 2 3 4) #(5 4 3 2))   ==> NIL
(notevery #'> #(1 2 3 4) #(5 4 3 2)) ==> T

 应该不难理解吧,想看作者啰嗦?

map函数:

 

(map 'vector #'* #(1 2 3 4 5) #(10 9 8 7 6)) ==> #(10 18 24 28 30)

(map-into a #'+ a b c)  将值赋给a

 map这东西怎么说捏,我一直当遍历用~

再给大家介绍一个强劲的递归函数reduce:

 

(reduce #'+ #(1 2 3 4 5 6 7 8 9 10)) ==> 55

 reduce带上前面的那些个参数,无限可能性啊!!

 

终于,跳出不是那么友好的数字索引式向量,咱来看看哈希表:允许用你喜欢的字符串来索引值(什么叫人性化?)话不多说,哈希表的基本玩法:

 

(defparameter *h* (make-hash-table))

(gethash 'foo *h*) ==> NIL

(setf (gethash 'foo *h*) 'quux)

(gethash 'foo *h*) ==> QUUX

 这里的gethash还有高级功能,在后面的章节会有给出,下面来个抢先看:

 

(defun show-value (key hash-table)
  (multiple-value-bind (value present) (gethash key hash-table)
    (if present
      (format nil "Value ~a actually present." value)
      (format nil "Value ~a because key not found." value))))

(setf (gethash 'bar *h*) nil) ; provide an explicit value of NIL

(show-value 'foo *h*) ==> "Value QUUX actually present."
(show-value 'bar *h*) ==> "Value NIL actually present."
(show-value 'baz *h*) ==> "Value NIL because key not found."

 看看就好,作为初学者,深究是伤脑筋的。如果会用到,自然而然~

与向量不大相同的是,由于并非数字索引的顺序结构,迭代、遍历哈希表也得换个方式:

 

(maphash #'(lambda (k v) (format t "~a => ~a~%" k v)) *h*)

(maphash #'(lambda (k v) (when (< v 10) (remhash k *h*))) *h*)

 还有种高级点的方法:

 

(loop for k being the hash-keys in *h* using (hash-value v)
  do (format t "~a => ~a~%" k v))

 这个后面章节还有详解。

 

本章的目的在开始出就表明了。不过对于Lisp,List或许真的才是王道吧---敬请期待下一章节<因为List,它被称为Lisp>!

 

好吧,我承认,偷懒省略了很多文字(我仍然坚称作者略显啰嗦~),难道只有List才能正确以待?

 

(未完待续)

0
0
分享到:
评论
1 楼 yolio2003 2011-11-03  
难道只有List才能正确以待? maybe

相关推荐

    实用Common.Lisp编程.pdf

    标题:“实用Common.Lisp编程.pdf” 描述:“实用Common.Lisp编程.pdf,2011.10出版” 从这些信息中,我们可以提炼出几个关键的知识点: ### Common Lisp语言简介 Common Lisp是一种高级的、通用的、多范式的编程...

    common-lisp-the-language-second-edition.PDF

    common-lisp-the-language-second-edition.PDF

    Practical Common Lisp-1st-2005

    《Practical Common Lisp-1st-2005》是一本专注于Common Lisp编程语言的实用书籍,作者Peter Seibel通过这本书向读者展示了如何使用Common Lisp来解决真实世界中的问题,强调程序员作为工程师和艺术家的双重身份,而...

    Practical Common Lisp笔记

    《Practical Common Lisp笔记》是一本深入探讨Common Lisp编程语言的实用教程。Common Lisp是一种功能强大的多范式编程语言,以其动态类型、宏系统和丰富的内置数据结构而闻名。这篇笔记详细记录了作者在学习过程中...

    emacs-lisp-intro-2.04.tar.gz

    因此,"emacs-lisp-intro-2.04"可能是一个教程、文档集合或是包含一系列示例代码的资源包,用于帮助初学者或开发者了解和学习Emacs Lisp。 由于描述中提到"py依赖包",这可能意味着这个Emacs Lisp的资源与Python...

    Lisp - 1990 - Common Lisp the Language, 2nd Edition - Steele, Guy L..pdf

    《Common Lisp the Language, 2nd Edition》是Guy L. Steele所著的一本关于Common Lisp编程语言的权威指南,作为第二版,它不仅继承了前一版的经典,还对Lisp语言进行了深入的扩展和更新。这本书是Lisp爱好者的宝贵...

    Successful Lisp How to Understand and Use Common Lisp - David B. Lamkins

    Provides practical advice for the construction of Common Lisp programs. Shows examples of how Common Lisp is best used. Illustrates and compares features of the most popular Common Lisp systems on ...

    实用Common.Lisp编程

    这本《Practical Common Lisp》之所以号称Practical,正是因为这本书大量介绍Common Lisp在现实世界中的各种应用方式,算是第一本「入世传教」的Common Lisp著作。《Practical Common Lisp》是目前最畅销的Common ...

    Practical Common Lisp pdf

    在压缩文件中,只有一个名为"practical_common_lisp.pdf"的文件,这是本书的PDF版本。由于原版PDF文件较大,经过7z压缩后,文件大小显著减小,便于下载和存储,但同时也能保持较好的图像和文字质量,满足读者的阅读...

    AutoLisp源文件--标注高程.LSP

    AutoLisp源文件--标注高程.LSP

    Common-Lisp.apk

    Common-Lisp.apk

    lisp小工具-fx.zip

    计算多个数字之和、计算多条线段长度之和、插入墙高标注、查询多段线顶点坐标并绘制、自动生成页码、绘制示坡线、插入排水箭头 https://blog.csdn.net/qq_24141055/article/details/121446354

    cad-lisp-3-表操作.LSP.lsp

    cad-lisp-3-表操作.LSP.lsp

    ANSI Common Lisp 中文翻译版.pdf

    ANSI Common Lisp 中文翻译版.pdf 此资源是 ANSI Common Lisp 的中文翻译版,涵盖了 Common Lisp 语言的基础知识和高级主题。该资源包含了 17 章节,从基础的列表、特殊数据结构、控制流程、函数、输入与输出、符号...

    practical common lisp.pdf

    ### 实用Common Lisp编程知识点概览 #### 一、书籍简介与评价 《实用Common Lisp》是一本面向实际程序员的Lisp语言入门书籍。作者Peter Seibel通过一系列真实世界的问题来展示Common Lisp的强大之处,强调工程性和...

    Lisp-music-player.rar_autolisp_autolisp程序_lisp_player

    标题中的“Lisp-music-player.rar”表明这是一个基于Lisp语言开发的音乐播放器软件,其源代码或可执行文件被压缩在RAR格式的文件中。RAR是一种流行的压缩格式,通常用于存储和分发多个文件,它允许用户将多个文件...

    Autolisp_Tools 免费版2019-03-28.zip

    《AutoLISP工具在CAD中的应用详解》 AutoLISP是一种特定于AutoCAD的编程语言,它是基于LISP(列表处理)语言的简化版本,专为CAD用户设计,旨在简化和自动化CAD操作。AutoLISP工具是CAD用户尤其是五金模具设计师的...

    Practical Common Lisp

    Practical Common Lisp 学习lisp的入门书籍

Global site tag (gtag.js) - Google Analytics