mirror of
https://github.com/beyondx/Notes.git
synced 2026-02-08 04:44:51 +08:00
Add New Notes
This commit is contained in:
363
Zim/Programme/lisp/Programming_in_Emacs_Lisp/Emacs_Lisp语言.txt
Normal file
363
Zim/Programme/lisp/Programming_in_Emacs_Lisp/Emacs_Lisp语言.txt
Normal file
@@ -0,0 +1,363 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2012-02-01T14:03:56+08:00
|
||||
|
||||
====== Emacs Lisp语言 ======
|
||||
Created Wednesday 01 February 2012
|
||||
|
||||
http://www.neu.edu.cn/cxsj/online/C1/Lisp%E8%AF%AD%E8%A8%80.htm
|
||||
|
||||
An Introduction to Programming in Emacs Lisp
|
||||
|
||||
这本书 emacs 里有,按“C-h i”,输入“m,Emacs Lisp Intro”就能看到。我把其中比较基础的部分挑出来翻译了一下。
|
||||
Lisp 语言的历史
|
||||
|
||||
Lisp 语言最早是在 20 世纪 50 年代末由麻省理工学院(MIT)为研究人工智能而开发的。Lisp 语言的强大使它在其它方面诸如编写编辑命令和__集成环境__等显示其优势。而 GNU Emacs Lisp 主要由 Maclisp 发展而来,该语言由 MIT 在 20 世纪 60 年代写成。它在某种程度上继承了 Common Lisp,而 Common Lisp 在 20 世纪 80 年代成了一种标准。但是 Emacs Lisp 要比 Common Lisp 简单得多。
|
||||
|
||||
===== 表处理 =====
|
||||
|
||||
Lisp 代表 **LISt Processing**,即表处理,这种编程语言用来处理由括号(即“(”和“)”)构成的列表。括号标记着表的边界,有时候一个列表之前加了一个撇号(即“'”,或者说是单引号)。__列表是 Lisp 语言的基础__。在 Lisp 语言中,一个列表看起来像这样:“'(rose violet daisy buttercup)”。也可以写成以下这种形式:
|
||||
|
||||
* '(rose
|
||||
* violet
|
||||
* daisy
|
||||
* buttercup)
|
||||
|
||||
这样看起来更为熟悉。这个表的元素是四种花的名字:rose,violet,daisy 和 buttercup。列表中也可以包含__数字__,如“'(1 2 3)”这种形式。在 Lisp 语言中,**数据和程序用同一种方式表示**,就是说它们都是由__符号(symbol)、数字或者其它表__组成的,中间用空格分开,两边加上括号。由于一个程序看起来像数据,它可以很容易地在其它地方用作数据。这是 Lisp 语言一个非常强大的特性。括号内带有分号和句号(即“;”和“.”)的句子不是列表。这里列举了另外一种形式的列表:
|
||||
|
||||
'(this list has (a list inside of it))
|
||||
|
||||
该表的元素包括 this,list,has 和一个子表 (a list inside of it)。这个子表由元素 a,list,inside,of,it 组成。
|
||||
|
||||
===== 原子 =====
|
||||
|
||||
在 Lisp 语言中,我们**将单词(word)叫做“原子”(atom)**,因为原子是不可分的。__一个列表可以由一个或多个原子组成,或者一个都没有__,不包含任何原子的列表形如“()”,也可以写作__“nil”__,被称为“空表”。空表既可看作是一个原子,也可以看作是一个列表。
|
||||
|
||||
__原子和列表都被称为“符号表达式”( symbolic expressions__ or, more concisely, __s-expressions)__。另外,由双引号标记的文本(甚至可以是句子或者段落)也是一个原子,如:
|
||||
|
||||
'(this list includes "text between quotation marks.")
|
||||
|
||||
这种形式的原子,即 "text between quotation marks." 也叫__字符串(string)__。
|
||||
另外__以单引号开头的list也是原子__。
|
||||
|
||||
常见的atom包括:number, string, symbol(如+、-、*、/、flower、name等**与函数或变量绑定的单词**), quoted list.
|
||||
__Lisp programming is mostly about symbols__ (and sometimes numbers) within lists.
|
||||
__a list is between parentheses, a string is between quotation marks, a symbol looks like a word, and a number looks like a number.__
|
||||
|
||||
===== 执行程序 =====
|
||||
|
||||
Lisp 语言中的__一个表就是一个可执行的程序__。如果你运行它(用 Lisp 术语来说就是“求值”),计算机会按如下三种情况中的一种执行:
|
||||
|
||||
* 一是什么也不做,仅仅__返回列表本身__
|
||||
* 二是返回错误信息
|
||||
* 三是将列表中的第一个符号作为一个命令进行一些操作。
|
||||
|
||||
如果在一个列表前加上“'”,当计算机处理该列表时就执行第一种情况,如果列表前没有“'”,则表中第一项具有特殊意义,它是一种命令(在 Lisp 语言中,这些命令被称为__函数__)。比如:
|
||||
|
||||
(+ 2 3)
|
||||
|
||||
这个列表执行(用术语来说就是“对列表求值”)的结果是 5,因为该表的第一项是“+”,表示对表中剩下的元素进行求和。如果在 Emacs 中执行,只需要将光标移动到右括号后方,然后执行“C-x C-e”即可,在回显区可以得到结果“5”。这样的方式是将命令递交给程序(在 Emacs 中被称为“Lisp 解释器”)的过程。
|
||||
|
||||
__evaluating a symbolic expression(原子或列表) most commonly causes the Lisp interpreter to return a value and perhaps carry out a side effect; or else produce an error.__
|
||||
|
||||
__将单引号放在list或symbol前面,表示不对这个list或symbol求值,而是返回紧跟引号的 printed representation。__
|
||||
|
||||
You can also__ evaluate an atom that is not part of a list__—one that is not surrounded by parentheses;
|
||||
__可以直接执行atom__,如number, string,symbol (**代表函数名的symbol不能直接执行**,必须要放在list中执行。)。
|
||||
|
||||
In the expressions, the parentheses tell the Lisp interpreter to treat buffer-name and buffer-file-name as __functions__; without the parentheses, the interpreter would attempt to evaluate the symbols as __variables__.
|
||||
|
||||
-> (set 'adf "dsf")
|
||||
"dsf"
|
||||
-> __adf__
|
||||
"dsf"
|
||||
->(defun demo ()
|
||||
-> (message "demo")
|
||||
->)
|
||||
demo
|
||||
->demo
|
||||
**Debugger entered**--Lisp error: (__void-variable__ demo)
|
||||
->__(demo)__
|
||||
"demo"
|
||||
->23
|
||||
23
|
||||
->"jdsf"
|
||||
"jdsf"
|
||||
->__'df__
|
||||
df
|
||||
|
||||
当在交互式模式中执行lisp代码出现错误时,emacs会自动进入调式模式:
|
||||
The error message is generated by__ a built-in GNU Emacs debugger__. We will `enter the debugger'. You get out of the debugger by typing __q__.
|
||||
---------- Buffer: *Backtrace* ----------
|
||||
**Debugger entered(表示进入了调式起模式窗口)**--Lisp error: (void-variable kk)
|
||||
eval(kk)
|
||||
eval-last-sexp-1(t)
|
||||
eval-last-sexp(t)
|
||||
eval-print-last-sexp()
|
||||
call-interactively(eval-print-last-sexp nil nil
|
||||
---------- Buffer: *Backtrace* ----------
|
||||
|
||||
You read the *Backtrace* buffer __from the bottom up__; it tells you what Emacs did.
|
||||
|
||||
===== 对子表求值 =====
|
||||
|
||||
如果对一个包含另外一个子表的列表进行求值,外部列表可以利用内部子表返回的值进行求值。这也解释了为什么__内部表达式先被求值__,因为这些返回值需要被外部表达式利用。如:
|
||||
|
||||
(+ 2 (+ 3 4))
|
||||
|
||||
执行上一列表可在回显区得到结果 9,因为 Lisp 解释器先在对内部表达式“(+ 3 4)”进行求值,得到 7,再对外部表达式“(+ 2 7)”进行求值得到 9。
|
||||
|
||||
===== 变量 =====
|
||||
|
||||
在 Emacs Lisp 中,__一个符号(symbol)可以指代一个值(value),也可以指代一个函数定义__。两者是有区别的,函数定义是一套计算机需要执行的指令,而一个值则是不同于其它事物的东西,比如一个数或者一个名字。一个符号的值可以是任何 Lisp 表达式,通常也被称为“变量”。一个符号还可以同时指代一个值和一个函数定义。__当一个符号与一个值绑定时,可以单独求值(例如作为函数的参数);但是当一个符号与一个函数定义绑定时,必须将其作为list的第一个元素才能调用该函数。__
|
||||
|
||||
===== 参数 =====
|
||||
|
||||
参数是指需要提交给函数的信息。不同的函数需要不同的__参数个数__,一些函数甚至不需要参数,另外不同的函数可能还需要不同的__参数类型__。例如:
|
||||
|
||||
(concat "abc" "def")
|
||||
|
||||
列表中“concat”函数将两个或多个字符串连接起来形成一个字符串,此函数所带的参数就是字符串,对该列表求值得到结果“abcdef”。一些函数如“concat”,“+”或者“*”可以有任意个数的参数,例如没有参数(即 0 个参数):
|
||||
|
||||
(+)
|
||||
(*)
|
||||
|
||||
对以上两个表达式求值,分别得到 0 和 1。与此类似的还有“message”函数,它用来给用户发送消息,如:
|
||||
|
||||
(message "My name is %s and I am %d years old." "Xiaodong Xu" 25)
|
||||
|
||||
对上表求值得到“My name is Xiaodong Xu and I am 25 years old.”。其中双引号“"”中的“%s”并不直接显示,而是搜寻该字符串后面的参数,它对第二个参数“Xiaodong Xu”求值并将它的值显示在“%s”所在的位置。同理,“%d”用第三个整数参数 25 来代替。
|
||||
|
||||
In Lisp, the arguments to a function are __the atoms(如number, string, variable, quoted list, quoted symbol) or lists__ that follow the function. __The values returned by the evaluation of these atoms or lists are passed to the function__. Different functions require different numbers of arguments; some functions require none at all.
|
||||
|
||||
当传递错误的参数类型时,emacs返回如下错误:
|
||||
---------- Buffer: *Backtrace* ----------
|
||||
Debugger entered--Lisp error:
|
||||
(wrong-type-argument number-or-marker-p hello)
|
||||
+(2 hello)
|
||||
eval((+ 2 (quote hello)))
|
||||
eval-last-sexp-1(nil)
|
||||
eval-last-sexp(nil)
|
||||
call-interactively(eval-last-sexp)
|
||||
---------- Buffer: *Backtrace* ----------
|
||||
|
||||
The first part of the error message is straightforward; it says **‘wrong type argument’**. Next comes the mysterious jargon __word ‘number-or-marker-p’. This word is trying to tell you what kind of argument the + expected.__
|
||||
|
||||
The symbol number-or-marker-p says that the Lisp interpreter is trying to determine whether the information presented it (the value of the argument) is __a number or a marker__ (**a special object representing a buffer position**). What it does is test to see whether the + is being given numbers to add. It also tests to see whether the argument is something called a marker, which is __a specific feature of Emacs Lisp__. (In Emacs, **locations in a buffer are recorded as markers**. When the mark is set with the C-@ or C-<SPC> command, its position is kept as a marker. **The mark can be considered a number**—the number of characters the location is from the beginning of the buffer.) In Emacs Lisp, + can be used to add the numeric value of marker positions as numbers.
|
||||
|
||||
The ‘p’ of number-or-marker-p is the embodiment of a practice started in the early days of Lisp programming. The __‘p’ stands for `predicate'__. In the jargon used by the early Lisp researchers, **a predicate refers to a function to determine whether some property is true or false**.
|
||||
|
||||
So the ‘p’ tells us that__ number-or-marker-p is the name of a function__ that determines whether it is true or false that the argument supplied is a number or a marker. Other Lisp symbols that end in ‘p’ include zerop, a function that tests whether its argument has the value of zero, and listp, a function that tests whether its argument is a list.
|
||||
|
||||
Finally, the last part of the error message is the **symbol hello**. __This is the value of the argument that was passed to __+. If the addition had been passed the correct type of object, the value passed would have been a number, such as 37, rather than a symbol like hello. But then you would not have got the error message.
|
||||
|
||||
===== 为变量赋值 =====
|
||||
|
||||
有好几种方法可以给变量赋值。一种是用“set”或者“setq”函数,另一种是用“let”函数。这种过程用术数来说就是__给一个变量“绑定”一个值__。用“set”函数赋值可用如下形式:
|
||||
|
||||
(set 'flowers '(rose violet daisy buttercup))
|
||||
|
||||
对上表求值时,列表“(rose violet daisy buttercup)”将在回显区显示,这是“**set”函数返回的值**。作为一种__附带效应(side-effcet)__,符号“flowers”被绑定到这个列表,也就是说“flowers”这个变量被这个列表所赋值。另外,这种过程也说明了 Lisp 解释器的一种附带效应,即赋值,是我们人所期望的主效应。__ every Lisp function must return a value if it does not get an error__, but it will only have a side effect if it is designed to have one.)
|
||||
|
||||
在实际应用中,几乎每个赋值语句的第一个参数都需要加上撇号“'”,由于这种情况很常见,set 和第一个参数前的“'”组合起来构成了一个特定的形式“setq”,如:
|
||||
|
||||
(setq carnivores '(lion tiger leopard))
|
||||
|
||||
这跟
|
||||
|
||||
(set 'carnivores '(lion tiger leopard))
|
||||
|
||||
完全等价。
|
||||
|
||||
“setq”也可用来__把不同的值赋给不同的变量__,第一个参数被赋给第二个参数的值,第三个参数被赋给第四个参数的值,以此类推,比如,要把一组“trees”赋给“trees”符号,同时把一组“herbivores”赋给“herbivores”符号,可以这样做:
|
||||
|
||||
(setq trees '(pine fir oak maple)
|
||||
herbivores '(gazelle antelope zebra))
|
||||
|
||||
这里用了“赋值”这个词语,另一种思维方式是“setq”把这个符号“指向”了这个列表。后者非常通用。
|
||||
|
||||
===== 函数 =====
|
||||
|
||||
除了一些用 C 语言写的“基本”函数,所有函数都是根据其它函数来定义的。定义函数时,可以在函数体中使用其它的函数,有些是用 Emacs Lisp 写的,有些是用 C 写的基本函数。用 C 写的目的是能够使 GNU Emacs 更容易在任何有 C 语言条件的计算机上运行。但是不必区分用 C 写的函数和用 Emacs Lisp 写的函数在使用上的区别。一个函数定义至多由五个部分组成: 1. 函数符号的名称。 2. 需要传递给函数的参数列表。如果没有参数,就是一个空表“()”。 3. 用于描述函数的文档(技术上可选,但是强烈建议写上)。 4. 可以使函数与人交互(即可以用“M-x”加上函数名称,或者用合适的键或键组合来调用函数)的表达式,可选。 5. 要求计算机执行的代码,即函数体。
|
||||
|
||||
函数定义可以写成如下模板:
|
||||
|
||||
(defun FUNCTION-NAME (ARGUMENTS...)
|
||||
"OPTIONAL-DOCUMENTATION..."
|
||||
(interactive ARGUMENT-PASSING-INFO) ; optional
|
||||
BODY...)
|
||||
|
||||
举个例子,定义一个乘以7的函数:
|
||||
|
||||
(defun multiply-by-seven (number)
|
||||
"Multiply NUMBER by seven."
|
||||
(* 7 number))
|
||||
|
||||
对此表求值后,我们就将“multiply-by-seven”这个函数装入 Emacs 了。此时再执行如下语句:
|
||||
|
||||
(multiply-by-seven 3)
|
||||
|
||||
在回显区可以得到结果 21。
|
||||
|
||||
“let”特殊形式
|
||||
|
||||
“let”表达式是 Lisp 语言的一种特殊形式,在大多数函数定义中都会被用到。“let”用于防止混淆,它创建一个“局部变量”的名字,这个名字将覆盖“let”表达式之外所有的同名变量。由“let”表达式创建的局部变量仅在“let”表达式内部保留,对外部没有影响。“let”一次可创建多个变量,并给每个变量创建一个初始值,初始值可以是某个特定的值,也可以是空(“nil”)。在“let”创建变量之后,将执行“let”主体中的代码并返回最后一个表达式的值。“let”表达式是由三部分组成的列表,第一部分是符号“let”,第二部分是一个列表,称为“变量列表”,每个元素可以是一个符号或者是一个二元列表(第一个元素是符号),第三部分是“let”表达式主体。可以用以下的模板来说明“let”表达式:
|
||||
|
||||
(let VARLIST BODY...)
|
||||
|
||||
如果变量列表由二元组列表组成(这种情况比较常见),“let”表达式的模板可以写成这样:
|
||||
|
||||
(let ((VARIABLE VALUE)
|
||||
(VARIABLE VALUE)
|
||||
...)
|
||||
BODY...)
|
||||
|
||||
下面给出创建并初始化两个变量“zebra”和“tiger”的表达式,“let”表达式主体是一个调用“message”函数的列表。
|
||||
|
||||
(let ((zebra 'stripes)
|
||||
(tiger 'fierce))
|
||||
(message "One kind of animal has %s and another is %s."
|
||||
zebra tiger))
|
||||
|
||||
“if”特殊形式
|
||||
|
||||
除“defun”和“let”之外,还有条件“if”。这种形式用于让计算机做判断。“if”背后的基本涵义是,“如果一个判断为真,那么一个表达式将被求值”。如果判断为假,表达式不会被求值。判断和执行部分是“if”列表的第二和第三部分,而第一个元素是“if”。不过,“if”表达式的判断部分常被称为“if部分”,而执行部分常被称为“then部分”。 “if”表达式也可以包含第三个可选参数,即“else部分”,用于判断为假的情况。在这种情况下,then 部分不会被求值,而 else 部分将被求值。这时“if”表达式可写成如下模板:
|
||||
|
||||
(if TRUE-OR-FALSE-TEST
|
||||
ACTION-TO-CARRY-OUT-IF-THE-TEST-RETURNS-TRUE
|
||||
ACTION-TO-CARRY-OUT-IF-THE-TEST-RETURNS-FALSE)
|
||||
|
||||
举个例子,
|
||||
|
||||
(if (> 4 5) ; if-part
|
||||
(message "5 is greater than 4!") ; then-part
|
||||
(message "4 is not greater than 5!")) ; else-part
|
||||
|
||||
判断为假时,表达返回“nil”。值得注意的是,“nil”在 Emacs Lisp 中有两种含义,一种代表空表,第二种代表假,而任何非空的值都被认为是真。
|
||||
|
||||
基础函数
|
||||
|
||||
在 Lisp 语言中,“car”,“cdr”和“cons”是基础函数。“cons”函数用于构建列表,“car”和“cdr”函数用于拆分列表。一个表的 CAR 就是该表的第一项,如:
|
||||
|
||||
(car '(rose violet daisy buttercup))
|
||||
|
||||
执行该表达式可得到“rose”。同理,一个表的 CDR 是该表的剩余部分,就是说,“cdr”函数返回该表第一项后面的部分。如
|
||||
|
||||
(cdr '(rose violet daisy buttercup))
|
||||
|
||||
执行得到“(violet daisy buttercup)”。同“car”一样,“cdr”既不会修改也不会从列表中删除元素,而仅仅是返回一个值。这是一个非常重要的特性。 “cons”函数与“car”和“cdr”相反,比如“cons”可以从一个三元表组成一个四元表:
|
||||
|
||||
(cons 'pine '(fir oak maple))
|
||||
|
||||
执行后在回显区得到结果“(pine fir oak maple)”。“cons”生成一个新列表,此列表中元素“pine”后面跟有原始列表中的元素“(fir oak maple)”。可以说“cons”将一个新元素放在一个表的开头,或者说是将其压在一个表上方,从而生成一个新的列表,而不改变原来列表的值。 “length”函数可以得到一个表中元素的个数,如:
|
||||
|
||||
(length (cons 'violet '(daisy buttercup)))
|
||||
|
||||
由于(cons 'violet '(daisy buttercup))返回一个三元列表,所以上表执行得到结果 3。空表的长度为 0。 “nthcdr”函数与“cdr”函数相关,它所做的是对一个列表重复执行“cdr”多次。如:
|
||||
|
||||
(nthcdr 2 '(pine fir oak maple))
|
||||
|
||||
得到结果“(oak maple)”。 “nth”函数返回一个列表的第 N 个元素,以 0 为起点,如:
|
||||
|
||||
(nth 1 '("one" "two" "three"))
|
||||
|
||||
得到结果“two”。 “setcar”和“setcdr”函数与“car”和“cdr”函数类似,但是它们会改变原有列表的值。
|
||||
|
||||
列表的实现
|
||||
|
||||
在 Lisp 语言中,原子用直接的方式记录,如果实际上不直接,在理论上也是直接的。对一个表就另当别论了,它是由一个指针对序列构成的。在这个序列中,每个指针对的第一个指针指向一个原子或者另一个列表,第二个指针指向另一个指针对,或者指向空符号(即“nil”,用于标记列表的结束)。一个指针本身是它指向的内容的地址,所以一个表就是一个地址的序列。比如列表“(rose violet buttercup)”的实现可以用下图简单表示:
|
||||
|
||||
_ _ _ _ _ _
|
||||
|___|___|--> |___|___|--> |___|___|--> nil
|
||||
| | |
|
||||
| | |
|
||||
--> rose --> violet --> buttercup
|
||||
|
||||
当一个变量被诸如“setq”之类的函数赋给一个列表时,它存储的是第一个长方形的地址。所以对表达式
|
||||
|
||||
(setq bouquet '(rose violet buttercup))
|
||||
|
||||
求值的结果可以这样表示:
|
||||
|
||||
bouquet
|
||||
|
|
||||
| _ _ _ _ _ _
|
||||
--> |___|___|--> |___|___|--> |___|___|--> nil
|
||||
| | |
|
||||
| | |
|
||||
--> rose --> violet --> buttercup
|
||||
|
||||
综合之前的概念,一个符号可以看作一个抽屉柜,用下图表示:
|
||||
|
||||
Chest of Drawers Contents of Drawers
|
||||
|
||||
__ o0O0o __
|
||||
/ \
|
||||
---------------------
|
||||
| directions to | [map to]
|
||||
| symbol name | bouquet
|
||||
| |
|
||||
+---------------------+
|
||||
| directions to |
|
||||
| symbol definition | [none]
|
||||
| |
|
||||
+---------------------+
|
||||
| directions to | [map to]
|
||||
| variable value | (rose violet buttercup)
|
||||
| |
|
||||
+---------------------+
|
||||
| directions to |
|
||||
| property list | [not described here]
|
||||
| |
|
||||
+---------------------+
|
||||
|/ \|
|
||||
|
||||
循环和递归
|
||||
|
||||
Emacs Lisp 有两种主要的方式来重复求值:一种用“while”循环,另一种用“递归”。 “while”表达式主要有以下几种形式:
|
||||
|
||||
简单形式
|
||||
|
||||
(while TRUE-OR-FALSE-TEST
|
||||
BODY...)
|
||||
|
||||
空表判断循环
|
||||
|
||||
(while TEST-WHETHER-LIST-IS-EMPTY
|
||||
BODY...
|
||||
SET-LIST-TO-CDR-OF-LIST)
|
||||
|
||||
增量计数循环
|
||||
|
||||
SET-COUNT-TO-INITIAL-VALUE
|
||||
(while (< count desired-number) ; true-or-false-test
|
||||
BODY...
|
||||
(setq count (1+ count))) ; incrementer
|
||||
|
||||
减量计数循环
|
||||
|
||||
SET-COUNT-TO-INITIAL-VALUE
|
||||
(while (> counter 0) ; true-or-false-test
|
||||
BODY...
|
||||
(setq counter (1- counter))) ; decrementer
|
||||
|
||||
递归函数包含了 Lisp 解释器用来调用自身函数的代码,但是这些函数的参数有着细微的差别。用术语说就是有着不同的“实体”。
|
||||
|
||||
一个简单的递归函数形如:
|
||||
|
||||
(defun NAME-OF-RECURSIVE-FUNCTION (ARGUMENT-LIST)
|
||||
"DOCUMENTATION..."
|
||||
(if DO-AGAIN-TEST
|
||||
BODY...
|
||||
(NAME-OF-RECURSIVE-FUNCTION
|
||||
NEXT-STEP-EXEXAMPLESSION)))
|
||||
|
||||
另一种形式是用“cond”函数:
|
||||
|
||||
(cond
|
||||
(FIRST-TRUE-OR-FALSE-TEST FIRST-CONSEQUENT)
|
||||
(SECOND-TRUE-OR-FALSE-TEST SECOND-CONSEQUENT)
|
||||
(THIRD-TRUE-OR-FALSE-TEST THIRD-CONSEQUENT)
|
||||
...)
|
||||
|
||||
如果第一个判断条件返回空(即为假),第一个判断语句将被跳过,然后再对第二个判断条件求值,以此类推。如果某个判断条件返回真,该判断语句将被求值。
|
||||
Reference in New Issue
Block a user