Re: (CL Net SNMP) SF.net SVN: cl-net-snmp: [213] onlisp-cn/trunk
Brought to you by:
binghe
From: Chun T. (binghe) <bin...@gm...> - 2008-04-05 15:35:19
|
Hi, Kov Chai Welcome back! Regards, Chun Tian (binghe) 在 2008-4-5,下午8:24, tch...@us... 写道: > Revision: 213 > http://cl-net-snmp.svn.sourceforge.net/cl-net-snmp/?rev=213&view=rev > Author: tchaikov > Date: 2008-04-05 05:24:54 -0700 (Sat, 05 Apr 2008) > > Log Message: > ----------- > * finish packages.tex > * add more indexes > > Modified Paths: > -------------- > onlisp-cn/trunk/10-other_macro_pitfalls.tex > onlisp-cn/trunk/11-classic_macros.tex > onlisp-cn/trunk/12-generalized_variables.tex > onlisp-cn/trunk/13-computation_at_compile-time.tex > onlisp-cn/trunk/4-utility_functions.tex > onlisp-cn/trunk/5-returning_functions.tex > onlisp-cn/trunk/7-macros.tex > onlisp-cn/trunk/9-variable_capture.tex > onlisp-cn/trunk/onlisp-cn.pdf > onlisp-cn/trunk/packages.tex > > Modified: onlisp-cn/trunk/10-other_macro_pitfalls.tex > =================================================================== > --- onlisp-cn/trunk/10-other_macro_pitfalls.tex 2008-04-02 12:44:57 > UTC (rev 212) > +++ onlisp-cn/trunk/10-other_macro_pitfalls.tex 2008-04-05 12:24:54 > UTC (rev 213) > @@ -115,10 +115,10 @@ > \label{sec:non-functional_expanders} > > Lisp 期望它所生成的宏展开式代码都是纯函数型的,就像第% > -~\ref{chap:functional_programming} 章 > 里描述的那样。展开器代码应该除了作为参数传给它的 > -形式之外不依赖任何其他东西,并且除了 > 通过返回值以外不应该对外界产生其他影响。 > +~\ref{chap:functional_programming} 章 > 里描述的那样。展开器代码除了作为参数传给它的 > +形式 (form) 之外不应该有其他依赖,并 > 且它影响外界的唯一渠道只能是通过其返回值。 > > -正如~\textsc{cltl2} (685 页) 所说,可 > 以安全地假定在编译代码中的宏调用将不会在运行期重新 > +正如~\textsc{cltl2} (685 页) 所述,在 > 编译代码中的宏调用可以保证不会在运行期重新 > 展开。另一方面,Common Lisp 并不保证一 > 个宏调用将在何时被展开,或者被展开多少次。如果一个宏 > 的展开式随上述两种情况而变的话,那么应 > 该将其视为错误。例如,假设我们想要统计某些宏被使用的 > 次数。我们不能简单地在源代码文件里做一 > 次搜索,因为宏可能会在由程序生成的代码里被调用。我们 > @@ -134,13 +134,13 @@ > 决定是否变换代码之前可能不得不展开表达式中的宏调用。 > > 作为一项通用规则,展开器代码除了其参数 > 之外不应依赖于其他任何东西。所以任何宏,比如说通过 > -字符串来构造展开式的那种,应当小心不 > 要对宏展开时所在的包做任何假设。这个间接但相当有代表性 > +字符串来构造展开式的那种,应当小心不 > 要对宏展开时所在的包做任何假设。下面这个简明但相当有代表性 > 的例子, > \begin{verbatim} > (defmacro string-call (opstring &rest args) ; wrong > `(,(intern opstring) ,@args)) > \end{verbatim} > -定义了一个宏,其接受一个操作符的打印 > 名称然后将其展开成一个对该操作符的调用: > +定义了一个宏,它接受一个操作符的打印 > 名称,然后将其展开成一个对该操作符的调用: > \begin{verbatim} >> (defun our+ (x y) (+ x y)) > OUR+ > @@ -305,8 +305,8 @@ > 如此这般地进入无限循环。一个宏展开成对 > 自身的调用是可以的,但不是这么用的。 > > 像~\texttt{nthb} 这样的递归宏其真正危险之处在 > 于它们通常在解释器里工作正常。然后当你最终 > -将你的程序跑起来然后试图编译它的时 > 候,它甚至不能编译。不仅如此,通常也不会有指示说问题出在 > -一个递归的宏身上;编译器只会简单地进 > 入无限循环然后留给你来找出究竟哪里搞错了。 > +将程序跑起来,然后试图编译它的时候, > 它甚至不能编译通过。不仅如此,通常也不会有提示,指出问题 > +来自一个递归的宏;编译器只会陷入无限循环,让你来找出究竟哪里搞错了。 > > 在这个案例中,\texttt{ntha} 是尾递归的。一个尾 > 递归函数可以轻易转换成一个迭代的等价形式, > \textsl{然后}用作宏的模型。一个像~\texttt{nthb} 的宏可以写成 > @@ -352,7 +352,7 @@ > 会调用全局定义的函数~\texttt{nth-fn},\texttt{nthe} 的每次展开 > 将在它里面拥有一个它自己 > 的该函数的版本。 > > -尽管你不能将递归函数直接转化成一个 > 宏,你却可以写一个宏其展开式是递归生成的。一个宏的展开 > +尽管你不能将递归函数直接转化成一个 > 宏,你却可以写出一个宏,让它的展开式是递归生成的。一个宏的展开 > 函数就是正常的~Lisp 函数,并且当然是可以递归的。例如, > 如果我们想定义内置~\texttt{or} 的 > 一个版本,我们将会需要用到一个递归的展开函数。 > > > Modified: onlisp-cn/trunk/11-classic_macros.tex > =================================================================== > --- onlisp-cn/trunk/11-classic_macros.tex 2008-04-02 12:44:57 UTC > (rev 212) > +++ onlisp-cn/trunk/11-classic_macros.tex 2008-04-05 12:24:54 UTC > (rev 213) > @@ -298,7 +298,7 @@ > \section{条件求值} > \label{sec:conditional_evaluation} > > -有时我们需要让宏调用中的某个参数仅在 > 特定条件下才被求值。这超出了函数的能力所及,因为函数总是 > +有时我们需要让宏调用中的某个参数仅在 > 特定条件下才被求值。这超出了函数的能力,因为函数总是 > 会求值其全部参数。诸如~\texttt{if}, > \texttt{and} 和~\texttt{cond} 这样的内置操作符能够 > 在保护其某些参数免于被求值,除非其他参 > 数返回特定值。例如,在这个表达式中 > \begin{verbatim} > @@ -385,6 +385,10 @@ > ((inq key t otherwise) `(t ,@rest)) > (t (error "bad >case clause"))))) > \end{verbatim} > +\index{in@\texttt{in}} > +\index{inq@\texttt{inq}} > +\index{in-if@\texttt{in-if}} > +\index{case@\texttt{>case}} > \caption{使用条件求值的宏} > \label{fig:macros_for_conditional_evaluation_2} > \end{figure} > @@ -515,11 +519,11 @@ > 同样在前面看到过~(\pageref{macro:for} 页),在一个数字范围上做迭代。 > > 通过定义这些宏展开到~\texttt{do} 上 > 面,我们就允许在它们的主体里使用~\texttt{go} 和 > -~\texttt{return}。正如~\texttt{do} 从 > ~\texttt{block} 和~\texttt{tagbody} 处继承 > +~\texttt{return}。正如~\texttt{do} 从 > ~\texttt{block} 和~\texttt{tagbody} > \index{tagbody@\texttt{tagbody}} 处继承 > 了这些权力,\texttt{while},\texttt{till} 和~ > \texttt{for} 也从~\texttt{do} 处继承 > 了它们。就像~ > \pageref{block_and_capture} 页上所解释的那样,\texttt{do} 内部隐含 > ~\texttt{block} 里的~\texttt{nil} 标签 > 将被图~\ref{fig:simple_iteration_macros} > -中的宏所捕捉。这更多的是一个特征而非 > ~bug,但它至少应该被明显地注意到。 > +中的宏所捕捉。虽然与其说这是个~bug, > 不如说它是个特性,但它至少应该被清楚地提到。 > > \begin{figure} > \begin{verbatim} > @@ -583,7 +587,7 @@ > \end{verbatim} > 两个宏都返回~\texttt{nil},除非一个显式的~\texttt{return} 出现 > 在主体中。 > > -这类迭代在需要处理某些路径概念的程序 > 里经常需要。后缀~\texttt{/o} 和~\texttt{/c} 用来 > +在需要处理某种路径表示的程序里,这类 > 迭代会经常用到。后缀~\texttt{/o} 和~\texttt{/c} 用来 > 说明这两个版本分别在开放和封闭的路径上 > 操作。举个例子,如果~\texttt{points} 是一个点的 > 列表而~\texttt{(drawline $x$ $y$)} 在~$x$ 和~$y$ 之间 > 画线,那么画一条从起点到终点的 > 路径我们写成 > @@ -899,8 +903,8 @@ > #'(lambda () (go-sailing)) > #'(lambda () (rob-bank))) > \end{verbatim} > -如果我们想要的全部就是条件求值,宏绝 > 对是不需要的。它们只是使程序更清晰罢了。尽管如此,当我们 > -需要介入参数形式,或者绑定作为参数传递的变量时宏就是必需的了。 > +如果所有我们想要的就是条件求值,那么 > 宏也不是非要不可的。它们只是使程序更清晰罢了。尽管如此,当我们 > +需要介入参数形式,或者绑定作为参数传递的变量时,宏就是必需的了。 > > 同样的情况也适用于那些用于迭代的宏。尽 > 管只有宏才提供这种定义一个可以带有表达式体的迭代结构 > 的方式,其实用函数来做迭代也是可能的, > 只要循环体被包装在那个函数里。\footnote{ > > Modified: onlisp-cn/trunk/12-generalized_variables.tex > =================================================================== > --- onlisp-cn/trunk/12-generalized_variables.tex 2008-04-02 12:44:57 > UTC (rev 212) > +++ onlisp-cn/trunk/12-generalized_variables.tex 2008-04-05 12:24:54 > UTC (rev 213) > @@ -3,12 +3,12 @@ > \label{chap:generalized_variables} > > 第~\ref{chap:when_to_use_macros} 章曾提到宏的一个优点是它们变 > 换其参数的能力。这类宏 > -中的一个是~\texttt{setf}。本章关注~\texttt{setf} 的内涵,然后 > 给出一些建立在其之上宏的 > -示例。 > +中的一个是~\texttt{setf}。本章关注~\texttt{setf} 的内涵,然后 > 给出一些宏作为例子,它们 > +将建立在~\texttt{setf} 的基础之上。 > > -在~\texttt{setf} 上编写正确的宏令人惊 > 奇地困难。为介绍这一主题,第一节将提供一个有略微 > -不正确的简单例子。接下来一节将解释该 > 宏的错误之处,然后展示如何修复它。第三和第四节展示建立 > -在~\texttt{setf} 之上的\utility的例 > 子,而最后一节解释如何定义你自己的~\texttt{setf} > +要在~\texttt{setf} 上编写正确无误的宏 > 是一件难事,其难度令人咋舌。为介绍这一主题,第一节将给出一个有点 > +小问题的简单例子。接下来一节将解释该 > 宏的错误之处,然后展示如何修复它。第三和第四节会介绍一些基于 > +~\texttt{setf} 的\utility的例子,而最 > 后一节会说明如何定义你自己的~\texttt{setf} > \inversion。 > > \section{概念} > @@ -54,12 +54,12 @@ > 完整的列表在~\textsc{cltl2} 的第~125 页。) > > 一个可以作为~\texttt{setf} 第一个参数的表达式 > 称为\textsl{\gv}。\gv已经成为一种强有力的 > -抽象。宏调用和\gv的相似之处在于任何能 > 展开成可逆引用的宏调用其本身可以可逆的\footnote{原文: > +抽象。宏调用和\gv的相似之处在于:任何 > 能展开成可逆引用的宏调用,其本身就会是可逆的\footnote{原文: > A macro call resembles a generalized variable in that any macro call > which expands into an invertible reference will itself be invertible. > 译者注:这意味着可逆的宏或者\gv都是可 > 以嵌套使用的,有些~``正则序'' 的意味。}。 > > -当我们也在~\texttt{setf} 之上编写我们 > 自己的宏时,这种组合可以导致明显更为清晰的程序。 > +当我们也在~\texttt{setf} 之上编写我们 > 自己的宏时,这种组合可以产生显然更为清爽的程序。 > 其中一个我们可以定义在~\texttt{setf} 上面的宏是~ > \texttt{toggle}\footnote{这个定义是 > 不正确的,下一节将给出解释。}, > \begin{verbatim} > @@ -110,9 +110,9 @@ > (toggle (friend-of x y)) > \end{verbatim} > > -\gv就像是美味的健康食品。它们能带来良 > 好模块化同时优美典雅的程序。如果你为你的数据结构提供 > +\gv就像是美味的健康食品。它们能让你的 > 程序良好地模块化,同时变得更为优雅。如果你为你的数据结构提供 > 了通过宏或者可逆函数的访问能力,其他模 > 块就可以使用~\texttt{setf} 来修改你的数据结构而无需 > -知道它们的表达细节。 > +知道它们的内部细节。 > > \section{多重求值问题} > \label{sec:the_multiple_evaluation_problem} > @@ -199,6 +199,10 @@ > > (define-modify-macro toggle2 () not) > \end{verbatim} > +\index{allf@\texttt{allf}} > +\index{nilf@\texttt{nilf}} > +\index{tf@\texttt{tf}} > +\index{toggle@\texttt{toggle}} > \caption{操作在\gv上的宏。} > \label{fig:macros_which_operate_on_generalized_variables} > \end{figure} > @@ -206,7 +210,7 @@ > \section{新的\utility} > \label{sec:new_utilities} > > -本节给出一些操作在\gv上的新\utility的 > 例子。它们必须是宏以便将参数完整地传给 > +本节给出一些操作在\gv上的新\utility的 > 例子。它们必须是宏,以便将参数完整地传给 > ~\texttt{setf}。 > > 图~ > \ref{fig:macros_which_operate_on_generalized_variables} 显示了构建在 > @@ -703,9 +707,9 @@ > \end{verbatim} > 该查询返回第二个值~\texttt{t},以表明在缓存中找到了答案。 > > -就像宏本身那样,gv是一种不寻常威力的 > 抽象。这里可能有更多需要探索的东西。个别用户已经发现了 > -一些使用\gv的方式可以带来更为优雅和强 > 有力的程序。但也有可能以全新的方式来使用 > -~\texttt{setf} 逆,或者探索类似的其他类别的有用的变换技术。 > +就像宏本身那样,\gv 是一种具有非凡威 > 力的抽象。这里可能还有更多有待探索的东 > 西。当然,有的用户很可能已经发现了 > +一些使用\gv的方法,通过它们能得到更为 > 优雅和强大的程序。但也有可能以全新的方式来使用 > +~\texttt{setf} 逆,或者找到其他类似的有用的变换技术。 > > %%% Local Variables: > %%% coding: utf-8 > > Modified: onlisp-cn/trunk/13-computation_at_compile-time.tex > =================================================================== > --- onlisp-cn/trunk/13-computation_at_compile-time.tex 2008-04-02 > 12:44:57 UTC (rev 212) > +++ onlisp-cn/trunk/13-computation_at_compile-time.tex 2008-04-05 > 12:24:54 UTC (rev 213) > @@ -4,8 +4,8 @@ > > 前面的章节描述了几类必须用宏来实现的操 > 作符。本章描述可以用函数来解决,但用宏可以更加高效 > 的一类问题。第~ > \ref{sec:macro_or_function} 节列出了在给定情形下使用宏的利弊。 > -有利的一面包括~``在编译期做计算''。通过定义一个操作符 > 为宏,有时你可在其展开时做掉它的 > -某些工作。本章关注那些充分利用这种可能性的宏。 > +有利的一面包括~``在编译期做计算''。通过定义一个操作符 > 为宏,有时你可在其展开时完成它的 > +某些工作。本章会关注那些充分利用这种可能性的宏。 > > \section{新的\utility} > \label{sec:new_utility_13} > @@ -18,6 +18,7 @@ > (defmacro avg (&rest args) > `(/ (+ ,@args) ,(length args))) > \end{verbatim} > +\index{avg@\texttt{avg}} > \caption{求平均值时转移计算。} > \label{fig:shifting_computation_when_finding_averages} > \end{figure} > @@ -51,6 +52,7 @@ > `(and ,a (> (incf ,hits) ,need))) > args))))) > \end{verbatim} > +\index{most-of@\texttt{most-of}} > \caption{转移和避开计算。} > \label{fig:shifting_and_avoiding_computation} > \end{figure} > @@ -111,6 +113,7 @@ > ,(car vars) ,var) > ,else))))) > \end{verbatim} > +\index{nthmost@\texttt{nthmost}} > \caption{使用编译期知道的参数。} > \label{fig:use_of_arguments_known_at_compile-time} > \end{figure} > > Modified: onlisp-cn/trunk/4-utility_functions.tex > =================================================================== > --- onlisp-cn/trunk/4-utility_functions.tex 2008-04-02 12:44:57 UTC > (rev 212) > +++ onlisp-cn/trunk/4-utility_functions.tex 2008-04-05 12:24:54 UTC > (rev 213) > @@ -19,17 +19,17 @@ > 感觉太小, 要是把它作为特定程序的一个组 > 成部分的话, 这段代码又太通用了, 这时就可以称之为 > \utility. 举例来说, 一个数据库不能称为\utility, 但是一个在列表上做单一 > 操作的函数就可以. > 大多数\utility和~Lisp 已有的函数和宏很 > 相似. 事实上, 许多~Common Lisp 内置的操作符就源自 > -\utility. 用于收集列表中所有满足条件元素的~\texttt{remove-if- > not} 函数, 在它成为~ > -Common Lisp 的一部分以前, 就被程序员们各自私下里定义了多年. > +\utility. 用于收集列表中所有满足条件元素的~\texttt{remove-if- > not}\index{remove-if-not@\texttt{remove-if-not}} 函数, > +在它成为~Common Lisp 的一部分以前, 就被程序员们各自私下里定义了多年. > > 学习编写\utility与其说是学习编写的技 > 术, 不如说是学习养成编写\utility的习惯. > \bup意味着同时写程序和编程语言. > -为了做好这一点, 你必须发展出一种能洞 > 察程序中缺少何种操作符的悟性. 你必须能够在看到一个程序时说, > +为了做好这一点, 你必须培养出一种能看 > 出程序中缺少何种操作符的洞察力. 你必须能够在看到一个程序时说, > ``啊, 其实你真正的意思是\textsl{这个}.'' > > 举个例子\label{page:nicknames}, 假设~ > \texttt{nicknames} 是这样一个函数, > -它接受一个名字然后构造出一个由源自 > -该名字的所有昵称组成的列表. 有了这个 > 函数, 我们怎样收集一个名字列表所对应的所有昵称呢? > +它接受一个名字,然后构造出一个列表,列表由 > +这个名字的所有昵称组成. 有了这个函 > 数, 我们怎样收集一个名字列表所对应的所有昵称呢? > 某个正在学习~Lisp 的人可能写出类似这样的函数: > \begin{verbatim} > (defun all-nicknames (names) > @@ -39,7 +39,7 @@ > (all-nicknames (cdr names))))) > \end{verbatim} > 而一个更有经验的~Lisp 程序员可能一看到这样的函数就会说 > ``啊, 其实你真正想要的是~ > \texttt{mapcan}\index{mapcan@\texttt{mapca}}.'' > -然后, 不再用定义并调用一个不得已的新 > 函数来找出一组人的所有昵称, 现在你只使用一个表达式足矣: > +然后, 不再被迫定义并调用一个新函数来 > 找出一组人的所有昵称, 现在只消用一个表达式就够了: > \begin{verbatim} > (mapcan #'nicknames people) > \end{verbatim} > @@ -140,7 +140,7 @@ > 来表示它. 即使一种编程构造开销较大, 如果我们写 > 代码的时候能比其他更便宜的方法省一半力气的话, 也会更喜欢用它. > \end{quote} > -在任何语言里, 这一``对简洁代码的倾向性''将带来麻烦, 除非允许用新的 > +在任何语言里, 这一``对简洁代码的倾向性''将招致麻烦, 除非允许用新的 > \utility来表达自身. 最简短的表达方式很少是最高效的. 如果我们想知道 > 一个列表是否比另一个列表更长, 原始的~Lisp 将诱使我们写出 > \begin{verbatim} > @@ -170,7 +170,7 @@ > \label{sec:operations_on_lists} > > 列表最初曾是~Lisp 的主要数据结构. 事实上, ``Lisp'' 这个名字就来自 > -``LISt Processing (列表处理)''. 不过请不要被这一历史事实所误解, > +``LISt Processing (列表处理)''. 不过请不要被这一历史事实所蒙蔽, > 尽管如此, Lisp 跟列表处理之间的关系并不比马球衫~(Polo shirts) > 和马球之间的关系好多少. 一个高度优化的~Common Lisp 程序里可能根本看不 > 到列表. > @@ -664,7 +664,7 @@ > (defun our-mapcan (fn &rest lsts) > (apply #'nconc (apply #'mapcar fn lsts))) > \end{verbatim} > -\index{mapcan@\texttt{mapcan}!草就而成的} > +\index{mapcan@\texttt{mapcan}!sketch of 草就而成的} > 由于~\texttt{mapcan} 用~ > \texttt{nconc} 把列表拼接在一起, 第一个参数返回的 > 列表最好是新创建的, 否则等下次看的时候它可能就变了. 这也是为什么 > ~\texttt{nicknames} (第~ > \pageref{page:nicknames} 页) 被定义成一个根据昵称 > @@ -676,24 +676,24 @@ > 下一个\utility, \texttt{mapcars}\index{mapcars@\texttt{mapcars}}, > 用于那些你想在一组列表上~mapcar 某个函数的场合. > \index{mapcar@\texttt{mapcar}!version for > multiple lists 用于多个列表的版本} > -如果我们有两个数列并且我们希望得到一个它们的平方根列表, 用原始~ > +如果我们有两个数列,并且希望得到一个它们的平方根列表, 用原始~ > Lisp 我们这样写: > \begin{verbatim} > (mapcar #'sqrt (append list1 list2)) > \end{verbatim} > -但这里的~cons 没有必要. 我们将~ > \texttt{list1} 和~\texttt{list2} 追加到 > -一起之后立即又把结果丢掉了. 借助~ > \texttt{mapcars} 我们用下列方法就可以得 > +但这里的~cons 没有必要. 我们将~\texttt{list1} 和~\texttt{list2} 串在 > +一起之后立即又把结果丢弃了. 借助~ > \texttt{mapcars} 我们用下面的方法就可以得 > 到相同结果: > \begin{verbatim} > (mapcars #'sqrt list1 list2) > \end{verbatim} > -而且还不用做额外的~cons 了. > +而且还避免了多余的~cons. > > 图~\ref{fig:mapping_functions} 中最后一个函数是 > ~\texttt{mapcar}\index{rmapcar@\texttt{rmapcar}}, 用于树的版本. > \index{mapcar@\texttt{mapcar}!version for trees 用于树的版本} > 它的名字, \texttt{rmapcar}, 是``递归~\texttt{mapcar}'' 的缩写, > -并且所有~\texttt{mapcar} 在扁平列表上做的事, 它可以在树上做到: > +并且所有~\texttt{mapcar} 在扁平列表上 > 能完成的功能, 它都可以在树上做到: > \begin{verbatim} >> (rmapcar #'princ '(1 2 (3 4 (5) 6) 7 (8 9))) > 123456789 > @@ -871,7 +871,7 @@ > 你很可能在做某件低效率的事情. 尽管如此, 在原型开发, 而 > 非产品软件中, 这类 > \utility还是有其用武之地的. > > -\section{致密性} > +\section{紧凑性} > \label{sec:density} > > 如果你的代码里使用了大量\utility, 某些 > 读者可能会抱怨它们难以理解. 那些还没能自如地使用 > @@ -903,8 +903,8 @@ > 阅读这种程序可能需要花一些力气, 但如果 > 不是这样写的话,你会需要花更多的精力 > 来读懂它们。 > > -有一种情况你应慎重地避免使用 > \utility: 如果你需要写一个和你代码其余部分无关的独 > -立分发的小程序. 一个\utility通常至少 > 要被使用两到三次才值得引入, 但在小程序里, > +有一种情况下,你应该有意地避免使用 > \utility,即: 如果你需要写一个小程序,它将独立于其余部分的代码发布. > +一个\utility通常至少要被使用两到三次才值得引入, 但在小程序里, > 如果一个\utility用得太少的话,可能就没有必要包含它了。 > > %%% Local Variables: > > Modified: onlisp-cn/trunk/5-returning_functions.tex > =================================================================== > --- onlisp-cn/trunk/5-returning_functions.tex 2008-04-02 12:44:57 > UTC (rev 212) > +++ onlisp-cn/trunk/5-returning_functions.tex 2008-04-05 12:24:54 > UTC (rev 213) > @@ -110,7 +110,7 @@ > 我们可以只用一半数量的函数就完成了全部的功能. > > 同样, \texttt{setf} 宏也增强了~Lisp 的 > 正交性. Lisp 的早期方言常会用成对的 > -函数分别实现读写数据的功能. 举例来说, 对于属性列表~(property-list), > 就会有 > +函数分别实现读数据和写数据的功能. 举 > 例来说, 对于属性列表~(property-list), 就会有 > 一个函数用来加入属性, 另一个函数则被用来查询它们. 在~Common Lisp 里面, > 我们只有后者, 即~\texttt{get} > \index{get@\texttt{get}}. 为了加入一个属性, 我们把~\texttt{get} 和 > ~\texttt{setf} 一同使用: > @@ -118,8 +118,8 @@ > (setf (get 'ball 'color) 'red) > \end{verbatim} > > -我们或许无法让~Common Lisp 变得更精 > 简, 但是我们可以作些努力达到差不多的效果: > -即使用这门语言的一个较小的子集. 可以 > 定义一些新的操作符, 就像~\texttt{complement} > +我们或许无法让~Common Lisp 变得更精 > 简, 但是我们可以作些努力达到差不多的效果,即: > +使用这门语言的一个较小的子集. 可以定 > 义一些新的操作符, 就像~\texttt{complement} > 和~\texttt{setf} 那样, 让它们帮助我们 > 更接近这个目标吗? 至少另外还有一种 > 方式让函数成对出现. 许多函数都有其破坏 > 性~(destructive) 的版本: 像~\texttt{remove-if} > 和~\texttt{delete-if}、 > \texttt{reverse} 和~\texttt{nreverse}、\texttt{append} > @@ -650,7 +650,7 @@ > 函数会在运行时让程序做一些不必要的工作。虽然~sharp-quoted 的 ~lambda > 表 > 达式是一个常量, 但是对构造函数的调用将 > 会在运行时估值。如果你真的必须在运行 > 时执行这个调用,可能使用构造函数并非上 > 策。不过,至少有的时候我们可以在事前就 > -调用这个构造函数。通过使用~ > \texttt{\#.},即~sharp-dot 读取宏,我们可以让 > +调用这个构造函数。通过使用~ > \texttt{\#.}\index{\#.@\texttt{\#.}},即~sharp-dot 读取宏,我们可以让 > 函数在读取时~(read-time) 就被构造出来。假设~\texttt{compose} > 和它的参数 > 在下面的表达式被读取时已经被定义了,那么我们可以这样写,举例如下: > \begin{verbatim} > > Modified: onlisp-cn/trunk/7-macros.tex > =================================================================== > --- onlisp-cn/trunk/7-macros.tex 2008-04-02 12:44:57 UTC (rev 212) > +++ onlisp-cn/trunk/7-macros.tex 2008-04-05 12:24:54 UTC (rev 213) > @@ -95,7 +95,7 @@ > \mbox{\texttt{`(a b c)} 等价于~\texttt{'(a b c)}.} > \end{equation*} > > -\bq只有当它和\comma,``\texttt{,}'', > 以及\commaat~符号,``\texttt{,@}'',组合出现时才 > +\bq只有当它和\comma,`` > \texttt{,}''\index{,@\texttt{,}},以及 > \commaat~符号,``\texttt{,@}'',组合出现时才 > 有用。如果说\bq创建了一个模板,那么 > \comma就在\bq中创建了一个槽位~(slot)。 > 一个\bq列表等价于将其元素引用起来调用一次~\texttt{list}。也就是, > \begin{equation*} > @@ -199,7 +199,7 @@ > 因为宏定义体看起来就像它生成的宏展开 > 式。要想理解第二个版本,不使用\bq的, > 你将不得不在头脑中跟踪展开式的构造过程。 > > -\commaat~符号,\texttt{,@},是\comma > 的变种,它的行为和\comma相似,但有一点不同: > +\commaat~符号,\texttt{,@}\index{,"@@ > \texttt{,"@}},是\comma的变种,它的行为和\comma相似,但有一点不同: > 它不只是像\comma那样将表达式的值插入到其所在的位置,\commaat~ > \textsl{拼接}到当前位置。 > 拼接可以认为是在插入时去掉了最外层的括号: > \begin{verbatim} > @@ -596,7 +596,7 @@ > 技术,所以某些读者可能需要稍后再回过头来读懂它。 > > 图~\ref{fig:a_sketch_of_defmacro} 中的 > 定义给出了关于宏的工作的相当精确的再现, > -但就像任何草稿一样它是不完整的。它不能正确地处理~ > \texttt{\&whole} 关键字。 > +但就像任何草稿一样它是不完整的。它不能正确地处理~\texttt{\&whole} > \index{whole@\texttt{\&whole}} 关键字。 > 而且~\texttt{defmacro} 实际上作为其第 > 一个参数的~\texttt{macro-function} 存储的 > 是一个带有两个变元的函数:宏调用本身, > 以及其发生时的词法环境。尽管如此,这些 > 缺失的特性只用在最刁钻的宏里才会用到。就算假设宏就是像图~ > > Modified: onlisp-cn/trunk/9-variable_capture.tex > =================================================================== > --- onlisp-cn/trunk/9-variable_capture.tex 2008-04-02 12:44:57 UTC > (rev 212) > +++ onlisp-cn/trunk/9-variable_capture.tex 2008-04-05 12:24:54 UTC > (rev 213) > @@ -89,7 +89,7 @@ > (/ vn wn)))) > \end{verbatim} > 如果用~\texttt{w = (b)} 来调用~ > \texttt{sample-ratio},那么它将会警告说它的一个 > -参数,只有一个元素,从统计上来讲没有 > 意义。但是当对~\texttt{gripe} 的调用被展开 > +参数只有一个元素,所得出的结果从统计 > 上来讲没有意义。但是当对~\texttt{gripe} 的调用被展开 > 时,\texttt{sample-ratio} 就好像被定义成: > \begin{verbatim} > (defun sample-ratio (v w) > @@ -421,7 +421,7 @@ > \pozhehao可以想象在同一个宏的嵌套实例中仍然会出现名字冲突。 > > 我们需要某种方式来\textsl{确保}一个符 > 号是唯一的。Common Lisp 函数~\texttt{gensym} > -的存在目的就在于此。它返回一个符号, > 称为一个\textsl{\gensym},可以保证不会和任何明确 > +的存在目的就在于此。\bubblenote它返回 > 一个符号,称为一个\textsl{\gensym},可以保证不会和任何明确 > 输入的或者由程序生成的符号~\texttt{eq} 相等。 > > Lisp 是如何确保这一点的呢?在~Common > Lisp 里,每一个包都保持一个该包知道的所有符号的列表。 > @@ -444,7 +444,7 @@ > \end{verbatim} > 它打印出来的东西基本上就相当于~``John Doe'' 的 > ~Lisp 等价物,一个为如何命名无关紧要的东西 > 构造的毫无意义的名字。并且为了确保我们 > 不会对此产生任何错觉,\gensym在显示上前缀一个星号-- > -冒号,一个特殊的读取宏~(read--macro) > 其存在是为了在我们试图第二次读取该\gensym时产生一个 > +冒号\index{\#:@\texttt{\#:}},一个特 > 殊的读取宏~(read--macro) 其存在是为了在我们试图第二次读取该\gensym时产 > 生一个 > 错误。 > > 在~\textsc{cltl2} Common Lisp 里,\gensym的打印形式中的数字来自~ > > Modified: onlisp-cn/trunk/onlisp-cn.pdf > =================================================================== > (Binary files differ) > > Modified: onlisp-cn/trunk/packages.tex > =================================================================== > --- onlisp-cn/trunk/packages.tex 2008-04-02 12:44:57 UTC (rev 212) > +++ onlisp-cn/trunk/packages.tex 2008-04-05 12:24:54 UTC (rev 213) > @@ -1,7 +1,7 @@ > -\chapter*{附录: 包~(packages)} > +\chapter{附录: 包~(packages)} > \label{chap:packages} > -包~(packages),是~Common Lisp 用来把代码组织成模块的方式。早期的~Lisp > -方言有一张符号表,名叫~ > \textsl{oblist}。在这张表里列出了系统中所有已经 > +包~(packages),是~Common Lisp 把代码组织成模块的方式。早期的~Lisp > +方言有一张符号表,即~\textsl{oblist}。在这张表里列出了系统中所有已经 > 读取到的符号。借助~oblist 里的符号表项,系统得以存取数据,诸如对象的 > 值,以及属性列表等。被保存在~oblist 里的符号被称为~\textsl{interned}。 > > @@ -10,10 +10,10 @@ > 为在一个包里的~intern 的符号只有在其被显式申明为能被其它包访问的时 > 候,它才能为外部访问~(除非用一些歪门邪道的招数)。 > > -包是一种~Liso 对象。当前包常常被保存 > 在一个名为~\texttt{*package*} 的全 > +包是一种~Lisp 对象。当前包常常被保存 > 在一个名为~\texttt{*package*} 的全 > 局变量里面。当~Common Lisp 启动时,当前包就是用户包:或者 > -叫~\texttt{user} (\textsc{CLTL1} 实 > 现),或者叫~\texttt{common-lisp-user} > -(\textsc{CLTL2} 实现)。 > +叫~\texttt{user} (\textsc{cltl1} 实 > 现),或者叫~\texttt{common-lisp-user} > +(\textsc{cltl2} 实现)。 > > 包一般使用它们的名字相互区别,而这些名 > 字采用的是字符串的形式。要知道当前包的包名, > 可以试试: > @@ -34,7 +34,7 @@ > 99 > \end{verbatim} > > -使用~\texttt{in-package},我们就可以 > 切换到另一个新的包,如果需要的话这 > +使用~\texttt{in-package}\index{in- > package@\texttt{in-package}},我们就可 > 以切换到另一个新的包,如果需要的话这 > 个包会被创建出来\footnote{在较早期的~Common Lisp 实现下,请省略 > 掉~\texttt{:use} 参数}: > \begin{verbatim} > @@ -72,8 +72,8 @@ > 这样做的话,就违背了模块化设计的初衷,而这正是包本来想要提供的。如果你 > 不得不使用双冒号来引用一个符号,这应该就是有人根本就不希望你引用它。 > > -一般来说,如果你之应该引用那些被~ > \textsl{expert} 的那些符号。通过把符号 > -从它所~intern 的包~export 出来,我们 > 就能让这个符号对其它包也是可见的。 > +一般来说,如果你只应该引用那些被~\textsl{expert} 了的那些符号。把符号 > +从它所属的包~export 出来,我们就能让这个符号对其它包变得可见。 > 要引出一个符号,我们可以调用~(你肯定已经猜到了) \texttt{export}: > \begin{verbatim} > MINE> (in-package 'common-lisp-user) > @@ -94,8 +94,154 @@ > 在导入了~\texttt{bar} 之后,我们可以完全不用加上任何包的限定符就能引用 > 它了。现在,这两个包共享了同一个符号\pozhehao再没有一个独立 > 的~\texttt{mine:bar} 了。 > -% p383 > > +万一已经有了一个会怎么样呢?在这种情 > 况下,\texttt{import} 调用会导致一 > +个错误,就像下面我们试着~import \texttt{foo} 时造成的错误一样: > +\begin{verbatim} > +MINE> (import 'common-lisp-user::foo) > +>>Error: FOO is already present in MINE. > +\end{verbatim} > + > +之前,我们在~\texttt{mine} 里对~\texttt{foo} 进行了一次不成功 > 的求值,这 > +次求值顺带着使得一个名为~\texttt{foo} 的符号 > 被加入了~\texttt{mine}。由 > +于这个符号在全局范围内还没有值,因此 > 产生了一个错误,但是输入符号名字的直接 > +后果就是使它被~intern 进了这个包。所以,当我们现在想把~\texttt{foo} > 引 > +进~\texttt{mine} 的时候,\texttt{mine} 里面已经有一个相同名字 > 的符号了。 > + > +通过让一个包\textsl{使用}~(use) 另一个包,我们也能批量的引入符号: > +\begin{verbatim} > +MINE> (use-package 'common-lisp-user) > +T > +\end{verbatim} > +这样,所有~user package 引出的符号就会自动地 > 被引进到~\texttt{mine} 里面 > +去了。(要是~user package 已经引出了~\texttt{foo} 的话,这个函 > 数调用也会 > +出一个错。) > + > +如~\textsc{cltl1} 所言,包含内建操作符和变量名字的包被称 > +为~\texttt{common-lisp} 而不是~ > \texttt{lisp},因此新一些的包在缺省情况 > +下已不再使用~\texttt{lisp} 包了。由于我们通过调用~\texttt{in-package} > +创建了~\texttt{mine},而在这次调用中也~\texttt{use} 了这个包,所以所 > +有~Common Lisp 的名字在~\texttt{mine} 中都是可见的: > +\begin{verbatim} > +MINE> #'cons > +#<Compiled-Function CONS 462A3E> > +\end{verbatim} > +在实际的编程中,你不得不让所有新编写 > 的包使用~\texttt{common-lisp} (或者 > +其他某个含~Lisp 操作符的包)。否则你甚至会没办法跳出这个新的 > +包。\footnote{译者注:即你不仅没有办法使用~\texttt{cons},更糟糕的 > + 是,你也不能用~\texttt{in-package} 切换到其它包。} > + > +一般来说,在编译后的代码中,不会像刚 > 才这样在顶层进行包的操作。更多的时 > +候,这些关于包的函数调用会被包含在源文件中。通常,只要 > +把~\texttt{in-package} 和~ > \texttt{defpackage}\index{defpackage@ > \texttt{defpackage}} 放在源文件的开头就可以 > +了。(\texttt{defpackage} 宏是~\textsc{cltl2} 里新引进 > 的,但是有些较老的 > +实现也提供了它。) 如果你要编写一个独立的包,下面列出了你可能会放在 > +对应的源文件最开始地方的代码: > +\begin{verbatim} > +(in-package ’my-application :use ’common-lisp) > +(defpackage my-application > + (:use common-lisp my-utilities) > + (:nicknames app) > + (:export win lose draw)) > +\end{verbatim} > +这会使得该文件里所有的代码,或者更准确地说,文件里所有的名字,都纳入 > +了~\texttt{my-application} 这个包。\texttt{my-application} 同时使用 > +了~\texttt{common-lisp} 和~ > \texttt{my-utilities},因此,不用加任何包名 > +作为前缀,所有被引出的符号都可以直接使用。 > + > +\texttt{my-application} 本身仅仅引出了三个符号,它们分别 > +是:\texttt{win}、\texttt{lose} 和~\texttt{draw}。由于在调 > +用~\texttt{in-package} 的时候,我们给~\texttt{my- > application} 取了一个 > +绰号~\texttt{app},在其它包里面的代码可以用类 > 似~\texttt{app:win} 的名字 > +来引用这些符号。 > + > +像这样的用包来提供的模块化的确有点不 > 自然。我们的包里面不是对象,而是一 > +堆名字。每个使用~\texttt{common- > lisp} 的包都引入了~\texttt{cons} 这个名 > +字,原因在于~\texttt{common-lisp} 包含了一个叫这个名字的函数。但是, > 这 > +样会导致一个名字叫~\texttt{cons} 的变量也在每个使 > +用~\texttt{common-lisp} 可见。这样的 > 事情同样也会在~Common Lisp 的其他名 > +字空间重演。如果包 (package) 这个机制 > 让你头痛,那么这就是一个最主要的原 > +因\pozhehao包不是基于对象的,它们基于名字。 > + > +和包相关的操作会发生在读取时 (read- > time),而非运行时。这可能会造成一些 > +困扰。我们输入的第二个表达式: > +\begin{verbatim} > +(symbol-package ’foo) > +\end{verbatim} > +之所以会返回它返回的那个值是因为: > \textsl{读取这个查询语句的同时,答案就被生成了}。 > +为了求值这个表达式,Lisp 必须先读入 > 它,这意味着要~intern~\texttt{foo}。 > + > +再来个例子,看看下面把两个表达式交换 > 顺序的结果,这两个表达式上面曾出现过: > +\begin{verbatim} > +MINE> (in-package ’common-lisp-user) > +#<Package "COMMON-LISP-USER" 4CD15E> > +> (export ’bar) > +\end{verbatim} > +通常来说,在顶层输入两个表达式的效果等价于把这两个表达式放在一 > +个~\texttt{progn}里面。不过这次有些不同。如果我们这样说 > +\begin{verbatim} > +MINE> (progn (in-package ’common-lisp-user) > + (export ’bar)) > +>>Error: MINE::BAR is not accessible in COMMON-LISP-USER. > +\end{verbatim} > +我们则会得到个错误提示。错误的原因在 > 于~\texttt{progn} 表达式在求值之前 > +就已经被~\texttt{read} 处理过了。当调用~\texttt{read} 时,当前包还 > +是~\texttt{mine},因而~\texttt{bar} 被认为是~ > \texttt{mine:bar}。 运行这 > +个表达式的效果就好像我们想要~export ~ > \texttt{mine:bar},而不是从 user 包 > +里~\texttt{common-lisp-user:bar}。 > + > +package 被如此定义,使得编写那些把符 > 号当作数据的程序成为一桩麻烦事。举个例 > 子,要是我们像下面那样定义~\texttt{noise}: > +\begin{verbatim} > +(in-package ’other :use ’common-lisp) > +(defpackage other > + (:use common-lisp) > + (:export noise)) > +(defun noise (animal) > + (case animal > + (dog ’woof) > + (cat ’meow) > + (pig ’oink))) > +\end{verbatim} > +这样的话,如果我们从另外一个包调用~ > \texttt{noise},同时传进去的参数是不 > +恰当的符号,\texttt{noise} 会走到~\texttt{case} 语句的末尾,并且返 > +回~\texttt{nil}: > +\begin{verbatim} > +OTHER> (in-package ’common-lisp-user) > +#<Package "COMMON-LISP-USER" 4CD15E> > +> (other:noise ’pig) > +NIL > +\end{verbatim} > +这是因为我们传进去的参数是~ > \texttt{common-lisp-user:pig} (这不是想冒犯 > +你),然而~\texttt{case} 接受~key 是~\texttt{other:pig}。为了 > +让~\texttt{noise} 如所想的那样工作, > 我们必须把里面用到的所有六个符号都 > +引出来,再在我们调用~\texttt{noise} 的包里面引入它们。 > + > +在这个例子里面,我们也可以通过使用关 > 键字而不是常规的符号,来绕过这个问 > +题。倘若~\texttt{noise} 像下面这样定义 > +\begin{verbatim} > +(defun noise (animal) > + (case animal > + (:dog :woof) > + (:cat :meow) > + (:pig :oink))) > +\end{verbatim} > +这样我们就能从任意一个包调用安全地调用这个函数了: > +\begin{verbatim} > +OTHER> (in-package ’common-lisp-user) > +#<Package "COMMON-LISP-USER" 4CD15E> > +> (other:noise :pig) > +:OINK > +\end{verbatim} > + > +关键字就像金子:普适而且自身就能表明其价值。它们不论在哪里都是可见 > +的,而且从来就没有要加上引用才能使用 > 它们的必要。符号驱动的函数几乎总是 > +应当使用关键字编写而成,比如~\texttt{defamaph} > +(第~\pageref{func:defanaph} 页)。 > + > +包里面有很多地方让人困惑不解。这里对 > 这一主题的介绍仅仅是冰山一角。要知 > +道所有的细节,请参考~\textsc{cltl2},第~11 章。 > + > + > %%% Local Variables: > %%% coding: utf-8 > %%% mode: latex > > > This was sent by the SourceForge.net collaborative development > platform, the world's largest Open Source development site. |