-
Notifications
You must be signed in to change notification settings - Fork 2
/
GoogleCLSG-zhTW.xml
3075 lines (3027 loc) · 143 KB
/
GoogleCLSG-zhTW.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-zhTW.xsl"?>
<GUIDE title="Google Common Lisp 風格指南">
<div align="center">
<p>
<a HREF="https://google.github.io/styleguide/lispguide.xml"
TARGET="_blank">
Google Common Lisp Style Guide Rev. 1.28</a> in Traditional 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>
當在包里命名(內部或外部)符號時,
你不應該將包的名稱作為前綴含在符號里。