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

lisp初体验-Practical Common Lisp笔记-6.语法及语义

    博客分类:
  • lisp
阅读更多
作为一本语言入门类书籍,终于要进入正题了!(不容易啊)

为什么满屏满目的括号?
这是一个问题么?对于初涉Lisp的人而言,是的。作者的解释就是不解释,熟悉了,你就懂了(好吧,其实是"详见后章")。总而言之,既然选择了,你就只能享受了(不许想歪!)。

与其他语言不同的是,lisp的编译器中有两个角色:reader和evaluator。reader负责把代码整成evaluator认识的东东,evaluator负责实现。(这里没太搞清楚,java什么的难道不是?或许神奇的宏在这儿做了些什么)

Lisp中的基本元素有:列表(list)和原子(atom)。列表是指由括号括起的东东:(),而原子呢就是除了列表之外的东东。不过常用的原子有这么三种:数字,字符串,名字。
数字:
123,1/4,1.0,-45,4/2,1.0e-4,1.0d0
上面的这些都归为数字,具体差异以后会讲。
字符串:
"abc","a\bc","a\"bc"
这些都是字符串了。
名字:
总的来说,变量名、方法名、宏名之类统称名字,用于指代他们本身。一个有效的命名不能出现这些东西:括号,单引号,双引号,反引号,逗号,冒号,分号,反斜杠,竖线(特殊字符)。当然,对于有编程经验的人而言,靠谱的字符串命名是相当有意义的,例如foo或者head。

这里有一个Lisp Form的概念,简单理解成lisp编译器认识的语句格式。其中最简单的莫过于原子了。其包含两个方面:符号和其他。
符号通常是个名字,比如:pi,代表圆周率。
其他通常就是数字、字符串了,有时候也可能是变量。
额,似乎有点绕(我也晕了)。不过不用担心,接下来就将三种常用的形式一一呈现:

函数调用:
(function-name argument*)

需要确保function-name是已经定义好的,后面跟随参数即可。例如:
(+ 1 2)  ----> 执行1+2
(* (+ 1 2) (- 3 4))   ---->执行1+2得到3,执行3-4得到-1,执行3×-1都得到-3

注意:lisp函数的调用与其他语言都不相同,函数名永远是在列表第一项,其后跟随参数。

特别运算:
并不是所有的功能都可以用函数调用来实现,比如:
(if x (format t "yes") (format t "no"))

如果按照函数调用的法则,简直就乱了套了,先要把后面的两个format执行了,再传入到If,明显与意图(如果x成立则yes,否则no)不符,所以需要独立。Lisp中类似if的一共定义了数十个,不过常用的不多,今后有涉及的话再介绍。这种特别运算的规则就是按照特别运算符自带的规则,比如这里的if:
(if test-form then-form [ else-form ])

再介绍两个特殊的运算符:quote,let
(quote (+ 1 2))  --->(+ 1 2)

等同于:
'(+ 1 2) -------> (+ 1 2)

(let ((x 10)) x)  #绑定变量,给x赋值10并返回x


宏:
又说到它了,而且还一天一个样。如果说上面两种都是依据规则而定,那么宏就是打破规则或者说是制定规则。
宏的应用分两步:进入宏,根据宏译成lisp的标准格式并lisp解析。是不是有点像前面介绍的lisp编译器的两个角色?宏实现了局部方言和标准的转换工作,或许可以称其为lisp中的lisp。不要晕,作者说,恩,之后还会有很多关于宏的东东!

这里再说说lisp的布尔数据:nil(false),t(truth)。
在lisp中,nil除了代表false外,也代表一个空的列表。在lisp中这些都是等值的:
nil, (), 'nil, '().
t只代表truth,t,'t也是等值的。

在lisp中用于等值判定的有四种:EQ,EQL,EQUAL, EQUALP(lisp不分大小写)
equal和equalp都可以理解成eql继承而来。所以,先比较下eq和eql:
eq 只适用于明确知道类型的比较,比如数字、字符串。
eql 相对没有那么严谨,可以比较相同类型的。
由此还引出了两个派别:"在可能的情况下用eq"派和"只用eql"派。(话说,我这而测不出差异来啊)
本文统一eql!
equal和equalp相对就更宽松了,比如字符串(equal "a" "a")值就是t,而equalp更进一步
(equalp 1 1.0)值也是t。

再来说说编码规范。
(some-function arg-with-a-long-name
               another-arg-with-an-even-longer-name)

(defun print-list (list)
  (dolist (i list)
    (format t "item: ~a~%" i)))

需要注意的几点:
1.缩进、对齐很重要。
2.收尾的闭括号紧接着最后的一个元素。
3.其实靠谱的编辑器都会自动做这个。

最后,关于代码注释:
lisp中的注释以分号(;)开始:
;;;; 全篇开始用四个分号.

;;; 段落开始用三个分号
;;; 然后紧接着就该是代码段落

(defun foo (x)
  (dotimes (i x)
    ;; 代码中的片段注释用两个分号开始
    ;; 紧接着的就该是代码片段
    (some-function-call)
    (another i)              ; 行注释用一个分号开始
    (and-another)           
    (baz)))

这些规则理论上并不强制,不过都是业内约定成俗的。从某种程度上来说,相信业内45年的经验吧~


(未完待续)
0
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics