-
Notifications
You must be signed in to change notification settings - Fork 2
/
GoogleCLSG-zhCN.xml
3075 lines (3027 loc) · 143 KB
/
GoogleCLSG-zhCN.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="styleguide-zh.xsl"?>
<GUIDE title="Google Common Lisp 风格指南">
<div align="center">
<p>
<a HREF="http://google-styleguide.googlecode.com/svn/trunk/lispguide.xml"
TARGET="_blank">
Google Common Lisp Style Guide Rev. 1.28</a> in Simplified Chinese.
License:
<a HREF="http://creativecommons.org/licenses/by/3.0/" TARGET="_blank">CC-By 3.0</a>.
</p>
</div>
<p align="right">
修订版号 1.28
</p>
<address>
Robert Brown
</address>
<address>
<a HREF="mailto:tunes@google.com">François-René Rideau</a>
</address>
<address>
纪念 Dan Weinreb
</address>
<p align="center">
<cite>模式意味着“我的语言不够用了。”</cite> ── Rich Hickey
</p>
<OVERVIEW>
<CATEGORY title="重要注意事项">
<STYLEPOINT title="注意:显示在本指南里所隐藏的信息">
<SUMMARY>
这个风格指南包含了许多表面上看不到的细节。他们由三角图示标记,可以在左边看到。按下它。你应该看到“万岁”出现在下方。
</SUMMARY>
<BODY>
<p>
万岁!现在你知道如何将点展开来获得更多细节。在文件的最上方也有一个“全部展开”的按钮。
</p>
</BODY>
</STYLEPOINT>
</CATEGORY>
<CATEGORY title="背景">
<p>
Common Lisp 是一个强大的<abbr title="multi-paradigm programming language">多范式程序语言</abbr>。能力越强,责任越大。
</p>
<p>
本指南推荐了格式化及风格化的选择,目的在于使你的代码更容易被其他人理解。针对我们在 Google 开发的内部应用及免费软件函式库,在改动之前你得先遵循这些准则。但是要注意的是,每个项目有自己的一套规则及惯例,违反或覆写了这些通用的准则;比如速度导向的 QPX 低费率搜索引擎就与 QRes 订位系统有着大相迳庭的风格。
</p>
<p>
如果你在 Google 以外的地方编写 Common Lisp 代码,我们邀请你一同来思考这些准则。在不与你自身优先考量起冲突的前提上,你可能会发现某些准则是很有用的。我们欢迎你评论及提供建设性的反馈,讨论如何改善这篇指南,并提供其它成功案例的风格。
</p>
<p>
本指南不是一个 Common Lisp 教程。关于语言的基本信息,请查阅 <a HREF="http://www.gigamonkeys.com/book/">Practical Common Lisp</a> 。关于语言参考手册,请查阅 <a HREF="http://www.lispworks.com/documentation/HyperSpec/Front/index.htm">Common Lisp HyperSpec</a> 。
至于更详细的风格指南,姑且看看 Peter Norvig 与 Kent Pitman 写的 <a HREF="http://norvig.com/luv-slides.ps">风格指南</a> 。
</p>
</CATEGORY>
</OVERVIEW>
<CATEGORY title="元準則">
<STYLEPOINT title="必须、应该、可能、别">
<SUMMARY>
每一个准则的重要程度,由下列来自 <a href="http://www.ietf.org/rfc/rfc2119.txt">RFC 2119</a> 的关键字及词组标示。
</SUMMARY>
<BODY>
<table>
<tr>
<th valign="top">必须 MUST</th>
<td>
<p>
MUST 或是 “REQUIRED”、“SHALL”,代表这是一个绝对得做的事儿。你必须徵询许可来违反一个 MUST。
</p>
</td>
</tr>
<tr>
<th valign="top">绝对不要 MUST NOT</th>
<td>
<p>
MUST NOT,或是 “SHALL NOT”,代表这是绝对不能做的事儿,你必须徵询许可来违反一个 MUST NOT。
</p>
</td>
</tr>
<tr>
<th valign="top">推荐 SHOULD</th>
<td>
<p>
SHOULD,或是形容词 “RECOMMENDED”,代表在特殊情况下也许有适当的理由可以违反准则的要求,但必须了解所有会影响到的事情,在选择另一个主题前审慎衡量。你必须徵询谅解来违反一个 SHOULD。
</p>
</td>
</tr>
<tr>
<th valign="top">不推荐 SHOULD NOT</th>
<td>
<p>
SHOULD NOT,或是片语 “NOT RECOMMENDED”,代表在特殊情况下也许有适当的理由可以违反准则的要求,但必须了解所有会影响到的事情,在选择另一个主题前审慎衡量。你必须徵询谅解来违反一个 SHOULD NOT。
</p>
</td>
</tr>
<tr>
<th valign="top">选择性 MAY</th>
<td>
<p>
MAY,或是形容词 “OPTIONAL”,代表某件事做不做完全取决于你。
</p>
</td>
</tr>
</table>
<p>
与 RFC 不同,我们在使用上列关键字时,不会将他们都转成大写。
</p>
<small>(译注:中文无法使用这些关键字,我尽力斟酌了行文中的口气,来达到效果。)</small>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="许可与谅解">
<SUMMARY>
有时候违反某些准则是有用甚至是必要的。在这些情况里,你必须向适当的人徵询许可或取得谅解。
</SUMMARY>
<BODY>
<p>
许可来自于项目的负责人。
</p>
<p>
在违反准则附近用注解来请求谅解,而你的代码审查者将授予谅解。原先的注解应由你签名,而审查者应在审查时,在注解里添加一个签名许可。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="惯例">
<SUMMARY>
必须遵守这些约定。这不是选择性的。
</SUMMARY>
<BODY>
<p>
某些准则启发自良好的普遍编程原则。某些准则启发自 Common Lisp 的技术特性。某些准则启发自一个技术理由,但在理由消灭后,准则仍被保留了下来。某些像是注解及缩排的准则,完全是基于惯例,而不是有明显的技术价值。在任何情况下,必须遵循这些准则,以及其他常见但尚未被纳入本文件的准则。
</p>
<p>
必须要遵循惯例。惯例对于可读性来说非常重要。当惯例默认被遵循时,违反惯例是某件需要注意的事发生了,并需要留意的信号。当惯例被有组织地违反时,违反惯例会成为需要被忽略的恼人噪音。
</p>
<p>
常规惯例是一种教化。目的使你仿效社群的习俗,这样便可更有效率的与现有成员合作。分辨出哪些是启发于技术性、或仅仅是惯例的准则仍然很有用,这样你知道可以在何时违反惯例来获得好的成效,以及何时寻求准则帮助你不落入陷阱。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="古老代码">
<SUMMARY>
编程时顺手修补老代码。
</SUMMARY>
<BODY>
<p>
我们许多的代码都是在准则存在前所写的。平常编程遇到违反准则的代码时,修复它们。
</p>
<p>
不要在没有警告其他开发者或协调的情况下进行大量修补,也不要使合并较大的分支变得比以前困难。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="未来议题">
<SUMMARY>
当前版本的文件没有考虑到许多额外标准化的主题,这留到之后的版本。
</SUMMARY>
<BODY>
<ul>
<li>
文件及目录结构
</li>
<li>
包与模组化
</li>
<li>
线程与锁
</li>
<li>
如何添加可配置的组件
</li>
<li>
CLOS 风格:initforms, 槽以及访问器名称,等等。
</li>
<li>
每个类可有的最大槽数的建议。
</li>
<li>
更多良好代码的具体例子:
<ul>
<li>
异常
</li>
<li>
事务(含重试)
</li>
<li>
XML
</li>
<li>
类型
</li>
<li>
封装或抽象
</li>
<li>
类别及槽名
</li>
<li>
等等。
</li>
</ul>
</li>
<li>
何时(不要)使用条件式编译:
<ul>
<li>
改动产品时
</li>
<li>
条件式调试或终端输出等。
</li>
<li>
“暂时性”注解掉代码块
</li>
<li>
等等。
</li>
</ul>
</li>
</ul>
</BODY>
</STYLEPOINT>
</CATEGORY>
<CATEGORY title="通用准则">
<STYLEPOINT title="原则">
<SUMMARY>
有某些基本原则,需要软件开发团队的每个开发者铭记在心。无论什么时候,觉得详细的准则不适当、感到疑惑或是自相矛盾时,回头看看这些原则来寻求指导:
<ul>
<li>
每一个开发者所写的代码必须让别的开发者容易阅读、理解及改动 ──── 即便最初的开发者已经不在了。(这是 “hit by a truck” 理论。)
</li>
<li>
大家的代码看起来要一致。理想上,不应该看到几行代码就认出,啊,这个风格是“Fred 写的代码”。
</li>
<li>
追求精准。
</li>
<li>
追求简洁。
</li>
<li>
KISS 原则(Keep It Simple, Stupid),简单就是美。
</li>
<li>
杀鸡焉用牛刀,用最适当的工具。
</li>
<li>
使用常识。
</li>
<li>
相关代码放在一起。将别人需要理解一部分代码所需的画面跳转减到最低。
</li>
</ul>
</SUMMARY>
<BODY>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="优先级">
<SUMMARY>
<p>
当抉择如何写出一段给定的代码时,依此优先序追求下列性质:
</p>
<ul>
<li>
客户的易用性。
</li>
<li>
可调试性或可测试性。
</li>
<li>
可读性或可理解性。
</li>
<li>
可扩充性或可修改性。
</li>
<li>
(运行期 Lisp 代码的)效率。
</li>
</ul>
</SUMMARY>
<BODY>
<p>
这些准则大部分都是直观的。
</p>
<p>
客户的易用性代表系统满足了客户的需求;如:需要处理客户的交易量,正常运作时的需求等等。
</p>
<p>
针对 Lisp 效率这一点,若是有两个同样复杂的选择,选运行较好的那个。(通常是构造比较少的那个,也就是从堆上配置了较少空间。)
</p>
<p>
给定两个选择,其中一个比另一个复杂,选择简单的那个,并分析出另一个有更好效能时,才重新审视当初的决定。
</p>
<p>
然而,避免过早优化。别为了给不常用到的代码提升速度而使复杂度上升。因为长期来说,鲜少运行的代码快不快不是那么重要。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="架构">
<SUMMARY>
要打造强韧且易维护的代码,这关系到如何将代码切分成组件,这些组件如何沟通,改版时这些改动如何传递。而最重要的是,程序员在改版时如何使这些组件互相沟通良好。
</SUMMARY>
<BODY>
<p>
如果你的工作会影响到其他的小组,或是可被小组之间重用,比如添加新组件会影响到其他小组(包括品管及运维),或是非本地作业的事情,在开始写代码前 ── 你必须至少写几段文字说明一下,并获得设计组或其他相关当事人的许可,不然在他们拒绝之后,准备好重头开始吧。
</p>
<p>
如果你不知道或不在乎这些议题,问问某些知道或在乎的人。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="使用函式库">
<SUMMARY>
通常最简单的办法是使用已存在的函式库。或还没存在的函式库。没有函式库的情况,我们鼓励你开发一个这样的函式库,但首先得未雨稠缪。
</SUMMARY>
<BODY>
<ul>
<li>
绝对不要贸然开始写一个新的函式库,除非你已查证没有可用的函式库存在,而新的函式库完成后可以解决或满足你的需求。这是一个违背<a HREF="http://zh.wikipedia.org/zh-cn/%E9%9D%9E%E6%88%91%E6%89%80%E5%89%B5">非我所创</a>症候群的规则,这个症候群在 Lisp 黑客圈里特别常见。
</li>
<li>
无论你采用是新的或古老的函式库,<em>必须</em>要获得许可,才能将第三方代码并入代码库。你必须在对的邮件组里讨论这个函式库的用途,并将你的代码交给此领域的专业人士审查,或是由(如果有的话) Lisp 函式库生态系统的人审查。并请准备好说明为什么这个特定的解决方案,比其他可用的函式库更好。
</li>
<li>
某些函式库的授权,与你正撰写的软件不兼容,则绝对不要将这个函式库认为是可用的。当心授权议题,或请教相关人士。
</li>
</ul>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="开源代码">
<SUMMARY>
<p>
如果你要写一个通用的函式库,或是改动一个存在的开源函式库,欢迎你将函式库与项目分开发布,并像是用其他的开源库一样导入你的函式库。
</p>
</SUMMARY>
<BODY>
<p>
用你的判断来分辨通用 vs 业务相关的代码,将通用的部份开源出来,而业务相关的部份保留为商业机密。
</p>
<p>
开源代码有许多好处,能促使第三方参与开发,使开发产品特色从用户角度出发,并使你诚实面对代码的品质。无论你写的是什么代码,你会需要维护他们的,并确保代码品质够好,能在产品上线时使用。开源正因为如此,不会有什么额外的负担。即便是(至少最初是如此)无法直接被第三方使用的代码。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="开发过程">
<SUMMARY>
开发过程超出了本文件的范围。然而开发者至少应记得下列几件小事:审查代码,撰写测试,去除警告,运行测试,避免大幅改动。
</SUMMARY>
<BODY>
<p>
</p>
<ul>
<li>
所有的代码改动必须经过审查。应该期待你的代码会被其他黑客审查,而你也会有机会去审查别人的代码。审查的部份标准,将会是代码需要遵守这份文件所载的编码标准。
</li>
<li>
你必须撰写测试,以及测试新撰写的代码,并记录你所修补的错误(bug)。每个 API 函数必须有单元测试,以及任何先前失败的例子。在前述事项做完之前,你的工作都不算完成。在评估工作任务时,必须算进撰写测试所花的时间。
</li>
<li>
代码编译后必须没有任何编译错误或是警告信息,等等。如果需要忽略编译器所抱怨的警告时,
将这些警告用 <code>UIOP:WITH-MUFFLED-COMPILER-CONDITIONS</code> 与 <code>UIOP:*UNINTERESTING-COMPILER-CONDITIONS*</code> 框架处理(部分是 <code>UIOP</code>,部分是 <code>ASDF 3</code>),将整个项目包起来,或是包覆单一的文件(使用 <code>ASDF</code> 的 <code>:around-compile</code> hook)。
</li>
<li>
所有的代码应该在一个适当的源代码管理系统检查,该系统可以在某种形式上,允许完整重新生成,某个已经布署(或可以布署)代码的版本、测试以及执行。
</li>
<li>
必须在运行测试前先测试单一的组件,只有在每个组件通过单元测试时,才可以提交代码。
</li>
<li>
应该将代码覆盖度纳入你的测试流程。如果测试不能涵盖所有新更新的代码,那么测试就是不足够的;无论有任何理由,一个测试无法覆盖的代码,需要清楚标明,并附上理由。
</li>
<li>
许多人在分支下开发。必须获得许可,再开始大幅度的改动。(比如大量的重新缩排)这样我们才可事先协调,并给予分支充裕的时间来回到主线上。
</li>
</ul>
</BODY>
</STYLEPOINT>
</CATEGORY>
<CATEGORY title="格式化">
<STYLEPOINT title="拼写与缩写">
<SUMMARY>
<p>
必须在注解里使用正确的拼写,而最重要要拼对的是函数的形参。
</p>
<p>
当数个正确拼写同时存在时(包括美式及英式英语),而开发者之间尚未有共识存在时,你应该选择较短的拼写。
</p>
<p>
必须只使用常见与领域相关的缩写,缩写保持一致。可以把受限作用域里的词法变量缩短,来避免符号名称过长。
</p>
</SUMMARY>
<BODY>
<p>
如果你不确定的话,查字典吧,或是 Google 下来检查拼写。或问问当地的专家。
</p>
<p>
下列是如何选择正确拼写的例子:
</p>
<ul>
<li>
使用 "complimentary" 表示免费饮料或大餐,
而不是 "complementary"。
</li>
<li>
使用 "existent" 以及 "nonexistent" 而不是 "existant"。
使用 "existence" 而不是 "existance"。
</li>
<li>
使用 "hierarchy" 而不是 "heirarchy"。
</li>
<li>
使用 "precede" 而不是 "preceed"。
</li>
<li>
使用 "weird" 而不是 "wierd"。
</li>
</ul>
<p>
下列是如何选择短的拼写的例子:
</p>
<ul>
<li>
使用 "canceled" 而不是 "cancelled"
</li>
<li>
使用 "queuing" 而不是 "queueing".
</li>
<li>
使用 "signaled" 而不是 "signalled";
</li>
<li>
使用 "traveled" 而不是 "travelled".
</li>
<li>
使用 "aluminum" 而不是 "aluminium"
</li>
<li>
使用 "oriented" 而不是 "orientated"
</li>
<li>
使用 "color" 而不是 "colour"
</li>
<li>
使用 "behavior" 而不是 "behaviour"
</li>
</ul>
<p>
位工业标准术语或是行话破例,包括了简单的拼写错误。
比如:
</p>
<ul>
<li>
在 HTTP 协议的上下文中,使用 "referer" 而不是 "referrer"
</li>
</ul>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="行长">
<SUMMARY>
应该要格式化你的源代码,使单行不超过 100 个字符。
</SUMMARY>
<BODY>
<p>
有某些行长限制总比没有好。古老的文字终端机使用 80 栏,但现在允许 100 栏似乎比较好,因为好的风格鼓励你使用具有描述性的变量以及函数名称。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="缩排">
<SUMMARY>
<p>
像配置好的 GNU Emacs 那样缩排你的代码。
</p>
<p>
项目里维护一个一致的缩排风格。
</p>
<p>
审慎的缩排会使代码更容易理解。
</p>
</SUMMARY>
<BODY>
<p>
一般 GNU Emacs 在缩排 Common Lisp 代码这件工作上表现的非常出色。也可以教会 GNU Emacs 如何缩排新定义的形式,比如给特定领域语言用的特殊规则。每个项目可能含有某些定制缩排的文件;使用它们吧。
</p>
<p>
在 Emacs 里缩排 Common Lisp 代码由 cl-ident 函式库提供。最新版的 cl-indent 打包在 <a HREF="http://www.common-lisp.net/project/slime/">SLIME</a> 里。安装 SLIME 之后,按照<a HREF="http://www.common-lisp.net/project/slime/doc/html/Loading-Contribs.html">这里的操作步骤</a>,通过把 slime-indentation 加到 slime-setup 会加载的 contrib 函式库里,将 Emacs 设定为自动载入 SLIME。
</p>
<p>
理想的情况下,使用 slime-indentation 提供的默认缩排设置。需要的话可以订制缩排参数,给项目建立一致的缩排风格。参数可以通过 define-common-lisp-style 里的 :variables 设置来订制。具体某些形式的缩排可以通过 define-common-lisp-style 里的 :indentation 设置来订制。这在创建宏、特殊操作符,这些与标准缩排(比如 defun、lables 或 let)不同的场景下格外有用。添加 <a HREF="http://www.gnu.org/software/emacs/manual/html_node/emacs/Hooks.html">hook</a> 到调用 common-lisp-set-style 的 'lisp-mode-hook 来正确设置适当的风格。
</p>
<p>
使用缩排让复杂的函数调用变得容易阅读。当调用一行放不下,或是函数接受太多参数时,考虑在参数之间插入新行,让每个参数都在独立的一行。不插入新行在某方面使得要知道函数接受多少参数,或参数从何开始又从何结束变得困难。
</p>
<BAD_CODE_SNIPPET>
;; 差劲
(do-something first-argument second-argument (lambda (x)
(frob x)) fourth-argument last-argument)
</BAD_CODE_SNIPPET>
<CODE_SNIPPET>
;; 较佳
(do-something first-argument
second-argument
#'(lambda (x) (frob x))
fourth-argument
last-argument)
</CODE_SNIPPET>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="文件表头">
<SUMMARY>
<p>
应该在每个源文件的最上方,注明维护者及其他重要信息。
</p>
<p>
不应该在源文件里附上版权与著作权的信息。
</p>
</SUMMARY>
<BODY>
<p>
每个源文件可以从简单描述下这个文件的内容开始。
</p>
<p>
在说明之后,每个文件应该用这个形式起步:
<code>(in-package :<em>package-name</em>)</code>
</p>
<p>
在 <code>in-package</code> 形式之后,
接着是任何与文件相关的声明,比如
<code>(declaim (optimize ...))</code>。
这些是 <code>ASDF</code> <code>:around-compile</code> hook 并没有涵盖到的声明。
</p>
<CODE_SNIPPET>
;;;; Variable length encoding for integers and floating point numbers.
(in-package #:varint)
(declaim #.*optimize-default*)
</CODE_SNIPPET>
<p>
不应该在文件顶端放著作权信息,著作权信息可以使用版本控制管理。文件顶端的著作权信息只会带来疑惑。而且当下的主要作者,最终可能不会是有最多贡献的人,之后也可能不再维护这个文件了。
</p>
<p>
不应该在单独的源代码文件里包含著作权信息。例外是当文件对外单独发布时。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="垂直空间">
<SUMMARY>
垂直空间:顶层级别的形式,一个空行。
</SUMMARY>
<BODY>
<p>
应该在顶层级别的形式留一个空行,比如函数定义。在特殊情况下,空行可以省略,简单的、密切相关的、同种类的定义形式,比如一组相关的类型声明,或是常量定义。
</p>
<CODE_SNIPPET>
(defconstant +mix32+ #x12b9b0a1 "pi, an arbitrary number")
(defconstant +mix64+ #x2b992ddfa23249d6 "more digits of pi")
(defconstant +golden-ratio32+ #x9e3779b9 "the golden ratio")
(defconstant +golden-ratio64+ #xe08c1d668b756f82 "more digits of the golden ratio")
(defmacro incf32 (x y)
"Like INCF, but for integers modulo 2**32"
`(setf ,x (logand (+ ,x ,y) #xffffffff)))
(defmacro incf64 (x y)
"Like INCF, but for integers modulo 2**64"
`(setf ,x (logand (+ ,x ,y) #xffffffffffffffff)))
</CODE_SNIPPET>
<p>
空行可以把复杂的函数切分成多个部分。一般来说,你应该要把大函数切成几个小函数,而不是添加垂直空间,让它读起来比较好读。如果你不能够切成小函数,你应该要使用 <code>;;</code> 注解,说明每个函数的部分各是干嘛的。
</p>
<p>
应该要努力保留顶层形式(含注解),但文档字串最好保持简短。超过一页的顶层形式很少见,有这种情况的话,确定用途是正当的。这也可以应用到 <code>eval-when</code> 里的形式,而不是限制 <code>eval-when</code> 这个形式。另一方面,<code>defpackage</code> 可能更长,因为可能含有长长的符号列表。
</p>
<p>
每个顶层形式应该要少于 61 行,包含注解,但文档字串不算在内。这是用于在 <code>eval-when</code> 里的每个形式,而不是 <code>eval-when</code> 本身。另外 <code>defpackage</code> 可以超過 61 行,因为它可能有长长的列表清单。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="水平空间">
<SUMMARY>
水平空间:括弧之间不要有空格、tab。
</SUMMARY>
<BODY>
<p>
绝对不要在括号或符号的前面或后面加上额外的空白。
</p>
<p>
绝对不要把右括号单写在一行。一组连续的尾随括号必须出现在同一行。
</p>
<BAD_CODE_SNIPPET>
;; 非常差劲
( defun factorial ( limit )
( let (( product 1 ))
( loop for i from 1 upto limit
do (setf product ( * product i ) ) )
product
)
)
</BAD_CODE_SNIPPET>
<CODE_SNIPPET>
;; 较佳
(defun factorial (limit)
(let ((product 1))
(loop for i from 1 upto limit
do (setf product (* product i)))
product))
</CODE_SNIPPET>
<p>
形式之间应该只用一个空格。
</p>
<p>
你不应该在多行连续的中间,使用空格来垂直排列形式。一个例外是当代码不垂直对齐,就看不出你要强调的重要性时。
</p>
<BAD_CODE_SNIPPET>
;; 差劲
(let* ((low 1)
(high 2)
(sum (+ (* low low) (* high high))))
...)
</BAD_CODE_SNIPPET>
<CODE_SNIPPET>
;; 较佳
(let* ((low 1)
(high 2)
(sum (+ (* low low) (* high high))))
...))
</CODE_SNIPPET>
<p>
你必须排列嵌套形式,如果他们超过一行的话。
</p>
<BAD_CODE_SNIPPET>
;; 差劲
(defun munge (a b c)
(* (+ a b)
c))
</BAD_CODE_SNIPPET>
<CODE_SNIPPET>
;; 较佳
(defun munge (a b c)
(* (+ a b)
c))
</CODE_SNIPPET>
<p>
惯例是一个绑定形式的主体,在第一行之后缩排两格。
任何在主体之前的绑定数据,通常缩排四格。
函数调用的参数与第一个参数对齐;
如果第一个参数自成一行,
则与函数名称对齐。
</p>
<CODE_SNIPPET>
(multiple-value-bind (a b c d)
(function-returning-four-values x y)
(declare (ignore c))
(something-using a)
(also-using b d))
</CODE_SNIPPET>
<p>
可有单独括号的例外献给数个定义之间的<code>eval-when</code> 形式;
在这个情况下,在闭括号附上一个注解 <code>; eval-when</code>。
</p>
<p>
必须设置好编辑器,使你在编辑文件时,避免插入 tab 字符。当编辑器不同意一个 Tab 是由几个空格代表时,Tab 会使你困惑。在 Emacs,输入 <code>(setq-default indent-tabs-mode nil)</code>。
</p>
</BODY>
</STYLEPOINT>
</CATEGORY>
<CATEGORY title="文档">
<STYLEPOINT title="钜细靡遗">
<SUMMARY>
所有可视函数都应该要使用文档字串,来解释如何使用你的代码。
</SUMMARY>
<BODY>
<p>
除非某段代码完全一目了然,不然就上一个文档字串(别名 docstring)。
</p>
<p>
文档字串生来就是给使用代码的程序员读的。他们可以从函数、类型、类别、变量以及宏取出,
并通过编程工具,如 IDE 来显示。或是通过在 REPL 下查询,如 <code>(describe 'foo)</code>;放在网上的文档或其他参考着作也可以在文档字串的基础上来创建。因此,文档字串是给你的 API 撰写文档的完美地点。应该描述如何使用代码(包括需要避开的陷阱),而不是代码是如何工作的(以及之后所需的工作),这两个是你该放在注解的东西。
</p>
<p>
当定义一个顶层及别的函数、类型、类别、变量以及宏时,提供一个文档字串。一般则是在程序语言允许加入文档的地方,添加文档字串。
</p>
<p>
关于函数,docstring 应该要描述函数的合约:
这个函数干什么,
这个函数的参数表示什么,
这个函数所返回的值,
这个函数可捕捉的状况。
应该在适当的抽象层级上来表达,解释意图,而不仅是解释语法。在文档字串里,将 Lisp 符号的名字转为大写,比如函数参数。打个比方,"The value of LENGTH should be an integer."
</p>
<CODE_SNIPPET>
(defun small-prime-number-p (n)
"Return T if N, an integer, is a prime number. Otherwise, return NIL."
(cond ((or (< n 2))
nil)
((= n 2)
t)
((divisorp 2 n)
nil)
(t
(loop for i from 3 upto (sqrt n) by 2
never (divisorp i n)))))
</CODE_SNIPPET>
<CODE_SNIPPET>
(defgeneric table-clear (table)
(:documentation
"Like clrhash, empties the TABLE of all
associations, and returns the table itself."))
</CODE_SNIPPET>
<p>
一个长的 docstring 通常用一句话的总结开始是有用的,接着才是 docstring 的主要内容。
</p>
<p>
当一个类型的名称被使用时,符号可以用反引号及单引号包围,反引号在前,单引号在后。Emacs 会将类型高亮,而高亮会变成读取器的线索,
<kbd>M-.</kbd> 会跳转到符号的定义。
</p>
<CODE_SNIPPET>
(defun bag-tag-expected-itinerary (bag-tag)
"Return a list of `legacy-pnr-pax-segment' objects representing
the expected itinerary of the `bag-tag' object, BAG-TAG."
...)
</CODE_SNIPPET>
<p>
当特化影响了方法的行为,超出通用函数的 docstring 所描述的内容时,应该给通用函数的每一个方法各自撰写文档。
</p>
<p>
当你修补了一个错误,思考看看修补后的代码是否正确,还是是错误的;如果不对的话,你必须添加一个注解,
从修补错误的观点来解释代码。如果可以的话,添加错误序号,也是推荐的。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="分号注解">
<SUMMARY>
你必须使用正确数量的分号来写注解。
</SUMMARY>
<BODY>
<p>
注解是给未来维护代码的人的说明。即便你是唯一能够看与接触到代码的人,即便你长生不老或是永远不离职,或是离职之后根本不管的人(并在这种万一的情况下使你的代码自行毁灭),你可能会发现给代码写注解是有帮助的。当然啦,在几个礼拜、月、年之后,回头看看代码时,你会发现当初写这个代码的人,完全与你不是同一个人,则你会感激当初自己有留下注解。
</p>
<p>
你必须给任何复杂的代码留注解,这样一来下个开发者才可以了解情况。(又来了,“hit by a truck” 理论。)
</p>
<p>
注解也可以作为指引阅读代码的人的一种方式,这样他们才知道这里有什么。
</p>
<ul>
<li>
文件表头及源文件里大段代码的重要注解,注解应该使用四个分号。
</li>
<li>
一个顶层级别的形式或是小组的顶层级别形式,注解应该使用三个分号。
</li>
<li>
在一个顶层级别的形式里,如果注解出现在行之间,注解应该使用两个分号。
</li>
<li>
如果是一个括号的备注且出现在行的最后,注解应该使用一个分号。你应该使用空格来分离注解与引用的代码,使得注解脱颖而出。你应该试着垂直排列相关的行尾注解。
</li>
</ul>
<CODE_SNIPPET>
;;;; project-euler.lisp
;;;; File-level comments or comments for large sections of code.
;;; Problems are described in more detail here: http://projecteuler.net/
;;; Divisibility
;;; Comments that describe a group of definitions.
(defun divisorp (d n)
(zerop (mod n d)))
(defun proper-divisors (n)
...)
(defun divisors (n)
(cons n (proper-divisors n)))
;;; Prime numbers
(defun small-prime-number-p (n)
(cond ((or (< n 2))
nil)
((= n 2) ; parenthetical remark here
t) ; continuation of the remark
((divisorp 2 n)
nil) ; different remark
;; Comment that applies to a section of code.
(t
(loop for i from 3 upto (sqrt n) by 2
never (divisorp i n)))))
</CODE_SNIPPET>
<p>
在分号与注解文字之间,应该留一个空格。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="标点与文法">
<SUMMARY>
应该使用正确的标点符号来撰写文档。
</SUMMARY>
<BODY>
<p>
当注解是一个完整的句子时,应该将第一个字大写,并用一个句号结束注解。普遍来说,你应该使用正确的标点符号。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="小心注意">
<SUMMARY>
对于需要特别注意的代码,必须遵循使用 TODO 注解的规范。
针对使用了晦涩形式的代码,必须要加上注解。
</SUMMARY>
<BODY>
<p>
对于需要特别留意的注解,像是未完成的代码、待办事项、问题、损坏及危险,
加上一个 TODO 注解指出问题的类型及本质,以及其他需要注意的事项。
</p>
<p>
TODO 注解的格式,由 <code>TODO</code>(全大写)开始,接着是姓名、E-mail 地址、或其他个人识别(与 <code>TODO</code> 相關的上下文)。
主要用途是有一致性的 <code>TODO</code>,可以在需要了解细节时搜索的到。<code>TODO</code>不代表该负责人会修复问题。所以创建 <code>TODO</code> 时,留的名字总是<code>TODO</code> 创建人的名字
</p>
<p>
当给注解签名时,应该使用用户名(针对公司内部代码),或是完整 email 地址(针对公司外部可见代码),而不只是名字的首字母缩写。
</p>
<CODE_SNIPPET>
;;--- TODO(george@gmail.com): Refactor to provide a better API.
</CODE_SNIPPET>
<p>
TODO 注解里具体指出时间或软件版本,请使用 <a HREF="http://www.w3.org/TR/NOTE-datetime">YYYY-MM-DD</a> 日期格式,这种格式使得自动处理更轻松,比如 2038-01-20 是 32 位有号 <code>time_t</code> 的最后一天。
</p>
<CODE_SNIPPET>
;;--- TODO(brown): Remove this code after release 1.7 or before 2012-11-30.
</CODE_SNIPPET>
<p>
对于使用了晦涩形式来完成工作的代码,必须加上注解叙述晦涩形式的用途,
以及晦涩形式完成了什么工作。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="特定领域语言">
<SUMMARY>
你应该给 DSL 及任何在 DSL 里的简短程序写文档。
</SUMMARY>
<BODY>
<p>
应该设计出同行容易阅读及理解的特定领域语言。
</p>
<p>
必须正确地给你的 DSL 写文档。
</p>
<p>
有的时候,你的 DSL 设计的相当简洁。
在这个情况里,如果每个程序从上下文中不是很直观的话,
用文档说明每个程序干了什么是很重要的。
</p>
<p>
值得注意的是,当使用正则表达式时(比如使用 <code>CL-PPCRE</code> 包),永远记得要留一条注解(通常在前一行加两个分号的注解),(最起码)要解释正则表达式做了什么,或是目的为何。注解不需要解释语法的所有细节,但应该让别人不需要解析正则表达式,就能理解你代码的逻辑是什么。
</p>
</BODY>
</STYLEPOINT>
</CATEGORY>
<CATEGORY title="命名">
<STYLEPOINT title="符号准则">
<SUMMARY>
使用小写。
遵循<a href="#拼写与缩写">拼写与缩写一节</a>的惯例。
遵循标点符号的惯例。
</SUMMARY>
<BODY>
<p>
所有的符号使用小写。一致地使用小写,除了可读性更高之外,也让查找符号名变得更容易。
</p>
<p>
注意 Common Lisp 会自动转换大小写,而对一个符号调用 <code>symbol-name</code> 时,会返回大写。由于这个转换大小写的特色,当你试着要分辨符号的大小写时,最终只会让你陷入困惑。但使用逃脱字符也是可以强迫符号成为小写的,不过你不应该使用这个功能,除非你需要与第三方软件协同操作。
</p>
<p>
在符号的单词之间放连字符。如果你不能很简单的说出标识符的名字,那符号大概命名的很差劲。
</p>
<p>
连字符必须用 <code>"-"</code>,不要用 <code>"/"</code> 或是 <code>"."</code>。除非你有一个无懈可击的理由,以及你的提议取得了来自其他黑客的许可。
</p>
<p>
参考<a href="#拼写与缩写">拼写与缩写</a>一节,以了解使用缩写的准则。
</p>
<BAD_CODE_SNIPPET>
;; 差劲
(defvar *default-username* "Ann")
(defvar *max-widget-cnt* 200)
</BAD_CODE_SNIPPET>
<CODE_SNIPPET>
;; 较佳
(defvar *default-user-name* "Ann")
(defvar *maximum-widget-count* 200)
</CODE_SNIPPET>
<p>
Common Lisp 在符号内使用标点符号是有惯例的。不应该在这些惯例之外,在符号内使用标点符号。
</p>
<p>
除非变量的作用域非常小,不要使用过短的名字,像是:<code>i</code> 以及 <code>zq</code>。
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="表明目的,而非内容">
<SUMMARY>
将变量用目的命名,而不是变量的内容。
</SUMMARY>
<BODY>
<p>
应该根据变量所意涵的概念命名,而不是根据概念在机器底层是怎么表示的来命名。
</p>
<p>
因此,你应该避免嵌入数据结构或结合类型名称,比如将 <code>list</code>, <code>array</code>,或是
<code>hash-table</code> 嵌入变量名,除非你正在写一个通用的演算法,适用于任何的列表、数组、哈希表,等等。在这个情况下,变量名有 <code>list</code> 或 <code>array</code>是完全没问题的。
</p>
<p>
当然啦,无论何时你有创造新种对象的目的时,应该使用 <code>DEFCLASS</code> 或 <code>DEFTYPE</code>,来引入新的抽象数据类型,操作这些对象的函数,可以通用地使用反映出抽象类型的名称。
</p>
<p>
举例来说,如果一个变量的值,总是一个 row(或是 <code>NIL</code>),叫它 <code> row </code> 或 <code>first-row</code> 是很好的,或者是其他相似的名字。<code>row</code> 被 <code>DEFTYPE</code>定义成 <code>STRING</code> 是没问题的。严格来说,因为你将细节抽象起来了,剩下的亮点是,它是一个 row。在这个上下文里,不应该将变量取名为 <code>STRING</code>,除非底层函数明确地操作 row 的内部结构,提供与 STRING 类似的抽象。
</p>
<p>
保持一致。如果变量在一个函数里命名成 <code>row</code>,且它的值被传给第二个函数,则将其称为 <code>row</code> 而不是 <code>value</code>。(这是一个实际情况)
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="全局变量与常量">
<SUMMARY>
根据惯例来命名全局变量。
</SUMMARY>
<BODY>
<p>
全局常量名应由加号开始,并以加号结束。
</p>
<p>
全局变量名应由星号开始,并以星号结束(在这个上下文里,星号又称为耳套「earmuffs」)。
</p>
<p>
在某些项目里,参数在普通情况下,通常不会被绑定或改动(但也许某些实验或例外情况会),应用一个钱号开始(但非钱号结束)。如果这样的惯例在你的项目里存在的话,应该一致地遵守。否则,应该避免这样子命名变量。
</p>
<p>
Common Lisp 没有全局词法变量,所以命名惯例是确保全局变量会被动态绑定,以及不会与局部变量名称重复。要捏造一个全局词法变量也是有可能的,只要有 <code>DEFINE-SYMBOL-MACRO</code> 以及用不同的方式命名全局变量。不应该使用这个技巧,除非你先发布一个函式库将它抽象起来。
</p>
<CODE_SNIPPET>
(defconstant +hash-results+ #xbd49d10d10cbee50)
(defvar *maximum-search-depth* 100)
</CODE_SNIPPET>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="判断式名称">
<SUMMARY>
判断式函数与变量的名字以 <code>"P"</code> 结尾。
</SUMMARY>
<BODY>
<p>
你应该将返回布尔值的函数与变量的
结尾以 <code>"P"</code> 或 <code>"-P"</code> 命名,
来表示他们是判断式。
一般来说,你应该使用,
函数名是一个单词时,使用 <code>"P"</code>;
超过一个单词时,使用 <code>"-P"</code>。
</p>
<p>
这个惯例的理由在
<a href="http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node69.html">the CLtL2 chapter on predicates</a> 给出。
</p>
<p>
为了要统一,你应该遵循上面的惯例,
而不是下面其中一个替代方案。
</p>
<p>
一个替代规则是,在某些已存在的包,
总是使用 <code>"-P"</code>。
另一个替代规则是,在某些已存在的包,
总是使用 <code>"?"</code>。
当你开发一个包时,你必须要与其它的包保持一致。
当你开始一个新包时,在没有非常充分记录你的理由之前,
你应该不要使用这些替代规则,
</p>
</BODY>
</STYLEPOINT>
<STYLEPOINT title="忽略函式库前缀">
<SUMMARY>
符号名不应该纳入一个函式库或包的前缀。
</SUMMARY>
<BODY>
<p>
当在包里命名(内部或外部)符号时,
你不应该将包的名称作为前缀含在符号里。