原文:Lisp Style Tips for the Beginner

本篇文章是一篇非正式的摘要,旨在帮助新手写出高效、易读的Lisp代码。

1 赋值

 

1.1 避免使用eval。赋值是Lisp内置的流程,所以你几乎没有任何理由自己调用它。如果你使用了它,那么请停下来并且重新思考一下你的问题,因为你解决问题的方法一定是错的。

1.2 熟悉宏。它是Lisp的一个重要特性,但是一个实现的很差的宏可以引起很严重的错误,而且对新手来讲很难解决。在你理解Lisp的解释器工作之前避免使用宏。绝不要编写宏来使代码“更高效”。

2 函数

 

2.1 绝不要像在C或者Pascal程序里那样实现函数。通常来讲,尽可能保持每个函数短小。每个函数应该只完成一项任务。实现很多小函数而不是几个大函数将会使你的代码更清晰、更模块化、更易重用。

2.2 在你开始实现某个函数之前,请确认Lisp是否已经实现了它!Common Lisp提供了数百个函数,宏以及特殊类型。请在开始你的编程工作前阅读Common Lisp: then Language (2nd Edition) 中的相关章节来熟悉他们。

2.3 如果你的函数返回不只一个结果,使用values而不是返回一个列表。如果你的函数不返回结果(C中的void),使用(values)来返回空值。

2.4 不要向你的代码中添加声明,除非你是一个Lisp老手并且完全确定那些变量将会被如何使用。

2.5 避免编写大块的case或者con块。考虑其他的方式,例如数据驱动的代码或者使用哈希表。

3 符号和变量

 

3.1 Lisp程序员倾向于使用小写字母。使用横杠来构成多词语的符号,例如multi-word-symbol。Lisp通常是冗长的,所以请习惯输入长长的符号名称!

3.2 全局变量使用*来标识,例如*standard-output*。

3.3 全局常量使用+来标识,例如+default-number+。

3.4 使用setf而不是setq。setf表示SET Field。setf是setq的一种更通用的形式,它能设定几乎所有你在Lisp中可以“指向”,或者引用的东西。这包括变量,数组位置,列表元素,哈希表入口,结构字段,以及对象。

4 列表

 

4.1 使用first,reset,second等等而不是使用car,cdr,cadr。

4.2 注意破坏性的列表操作。他们高效但是也是代码中隐蔽bug的来源。

4.3 记住length必须和指针一起来找到列表的末尾。如果你多次调用了某个对象的length,那么请使用变量而不是多次调用length。

4.4 记住append会拷贝它的参数。避免在循环中使用append来向列表中添加元素。在loop中使用collect语句,或者将元素push到列表里然后使用nreverse将列表重新排序。

最坏:

(let ((result ()))
  (dolist (x list)
    (setf result (append result (list x))))
  result)

好一些:

(let ((result ()))
  (dolist (x list)
    (push x result))
  (nreverse result))

最好:

(loop for x in list collect x)

4.5 记住copy只拷贝列表最外层的元素。使用copy-tree来拷贝整个列表。

5 条件

 

5.1 如果你并不需要条件语句的值,并且没有else分句,那么使用when和unless。否则,如果条件语句简单则使用if,否则使用cond。对于cond要在其结尾添加t语句。

5.2 Lisp程序员经常使用函数和and、or来实现简单的条件计算。例如:

(and x (setf y t))

等价于

(when x
  (setf y t))

(or x (setf y t))

等价于

(unless x
  (setf y t))

6 循环

避免使用do,这会使代码难以阅读。对于简单的循环,dotimes和dolist都不错。如果你需要保存,存储或者修改循环的结果,那么loop几乎可能是最简洁、最高效的结构。一些Lisp程序员因为它“不像lisp”而拒绝使用它。然而loop是一个好用并且非常强大的特性。在花费了十分钟使用do之后,大部分Lisp程序员都很高兴loop的存在!

7 注释

请写注释。对于整体功能以及有点复杂的代码进行注释(你会在2星期内忘掉你是如何实现他们的)。Lisp提供了两种不同的注释。分号标记单行注释。Lisp会忽略掉分号之后的整行。分号并不需要一定在行首。例如:

; this is a comment
(abs x) ; need absolute value here!

习惯上,Lisp程序员会区分单个、两个、三个分号的注释。单分号表示后面的注释用来解释同一行内的语句。两个分号表示该注释用来描述程序在该点的状态,或者用来解释下面这部分代码。两分号的注释需要跟它注释的代码具有相同的缩进。三分号注释提供全局的解释,并且总是在最左侧开始。例如:

;;;  the next  functions do various sorts of frobbing
(defun frob1 (num)
  ;; return double frob of num
  (let ((tmp (random num)))      ; breaks , fix!
    (double-frob tmp num :with-good-luck t)))

在#|和|#之间的文本将被注释掉。Lisp将会忽略掉其中所有的文本。#||#通常用来注释掉文件或者函数中的大块的代码。例如:

#|
;;; i think this function is obsolete.
(defun frob2 (list)
  (frob-aux (first list)))
|#

将会注释掉一个不再使用的函数定义。

8 格式和缩进

坏格式的Lisp代码是很难阅读的。一份易读的Lisp代码的最重要的前提条件是使用简单、统一风格的缩进。有些编辑器,例如Emacs或者Fred,他们理解Lisp语意并且会替你完成这项工作。其他的文本编辑器,例如NeXTStep的Edit.app,除了括号匹配之外则不理解Lisp。即使你使用的编辑器并不能帮助你执行Lisp的缩进,你也应该自己花时间来格式化你的代码。下面是一些非常简单的规则:

8.1 如果你的编辑器提供多种字体,你都需要将你的Lisp代码使用一种等宽的字体来显示,例如Courier。

8.2 避免编写出长度超过70个字符的行。不要假设你的读者会把窗口大小调整的和你一样,而一旦换行则很难进行阅读。

8.3 defun、defmacro或者let语句的语句体应该向里缩进两个空格。下面的例子中,defun和let都缩进了两列。let的缩进是相对let的定义开始的:

(defun foo (a b &aux c)
  (setf c (* a b))
  (let ((d )
        (e (if (zerop b) t nil)))
    (check-compatibility d c)
    (foo-aux a b c d e)))

8.4 如果函数的参数占用了超过一行,后面的参数要和第一个参数保持相同缩进。下面的例子可以清晰的展示出compute-closure一共有三个参数:

(setf s (compute-closure this-function
                         (list other-function my-method)
                         ))

8.5 不要使用tab来缩进,并且不要在单独一行结束括号。C语言的风格在Lisp里看起来很傻。例如下面的代码:

(defun my_Foo (x)
    (let ((y (* x ))
         )
        (values y x)
    )
)

在Lisp程序员看来这样的格式更好:

(defun my-foo (x)
  (let ((y (* x )))
    (values y x)))

Date: 2015-09-25

Author: slegetank

Created: 2016-09-25 Sun 12:28

Validate

翻译:Lisp Style Tips for the Beginner - Heinrich Taube的更多相关文章

  1. Common Lisp Style Guide - Ariel Networks Labs

    Common Lisp Style Guide - Ariel Networks Labs Common Lisp Style Guide

  2. 【翻译】C# Tips & Tricks: Weak References - When and How to Use Them

    原文:C# Tips & Tricks: Weak References - When and How to Use Them Sometimes you have an object whi ...

  3. [翻译]Review——24 tips for React Native you probably want to know

    Post author: Albert Gao Post link: http://www.albertgao.xyz/2018/05/30/24-tips-for-react-native-you- ...

  4. 集成google翻译的小tips

    文章首发于github.io 2018-08-04 12:43:20 google翻译的强大,就像我们公司的slogan : "让语言无国界,让世人心相通" 友情提示: googl ...

  5. Asp.NET调用有道翻译API

    调用有道API进行翻译,如图: HTML: <%@ Page Language="C#" AutoEventWireup="true" CodeFile= ...

  6. 【Android XML】Android XML 转 Java Code 系列之 style(3)

    最近一个月把代码重构了一遍, 感觉舒服多了, 但总体开发进度没有变化.. 今天聊聊把style属性转换成Java代码的办法 先说结论: 引用系统style是无法完美的实现的, 我们如果有写成Java代 ...

  7. Github上的1000多本免费电子书重磅来袭!

    Github上的1000多本免费电子书重磅来袭!   以前 StackOverFlow 也给出了一个免费电子书列表,现在在Github上可以看到时刻保持更新的列表了. 瞥一眼下面的书籍分类目录,你就能 ...

  8. Github 的一个免费编程书籍列表

    Index Ada Agda Alef Android APL Arduino ASP.NET MVC Assembly Language Non-X86 AutoHotkey Autotools A ...

  9. layer弹出信息框API

    首先向大家推荐layer,在这里也非常感谢贤心的贡献,非常不错的信息框及弹出层解决方案,为一些项目的前端开发提高了很大的效率,希望layer 越办越好! 下面是API,呵呵,官方抄袭过来的,为了自己看 ...

随机推荐

  1. ArcGIS 裁剪地图显示范围

    在argmap工具中,图层属性中,数据框选择“裁剪选项”,“指定范围”,根据一个要素的轮廓,即可以选择需要全屏显示的图层“要素的轮廓”,确定以后地图就自动居中显示,请注意要排除掉超出范围的图层,否则发 ...

  2. js获取屏幕大小

    1.js获取屏幕大小 <html> <script> function a(){ document.write( "屏幕分辨率为:"+screen.widt ...

  3. PHP 操作MySQL———来自copy

    学习要点:1.PHP 连接到MySQL2.增删改查3.其他常用函数 如果你已经具有了使用PHP.SQL 和MySQL 的丰富经验,现在就可以把所有这些技术组合在一起.PHP 与MySQL 之间稳固的集 ...

  4. hdu 1312

    原题链接 题意:“@”为起点,“.”为路,求可以走的格子有多少个(包括起点) 水题 bfs搜一发 思路:只有可以走的节点才能进入队列,所以每次出队列时ans+1就可以了(没有退出条件,所有可进入的节点 ...

  5. [LintCode] Coins in a Line 一条线上的硬币

    There are n coins in a line. Two players take turns to take one or two coins from right side until t ...

  6. SPARK 中 DriverMemory和ExecutorMemory

    spark中,不论spark-shell还是spark-submit,都可以设置memory大小,但是有的同学会发现有两个memory可以设置.分别是driver memory 和executor m ...

  7. IIS 注册4.0 Framework

    打开cmd ,运行如下命令  C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i      

  8. Joomla 3.2.0 - 3.4.4 无限制SQL注入漏洞

    http://www.sebug.net/vuldb/ssvid-89680#0-tsina-1-18081-397232819ff9a47a7b7e80a40613cfe1 http://10.21 ...

  9. springmvc+mybatis用多选框批量删除的功能Java代码

    今天写了一个批量删除的功能,在后台传值过程中一直出错,最终还是请教了北京的一位高手帮我解决的,在此首先要好好感谢他,以后我有幸能帮助别人的话,决不推辞. 废话不说,直接进入正题,我会将在编写过程中出现 ...

  10. # 20145334赵文豪 《Java程序设计》第7周学习总结

    20145334赵文豪 <Java程序设计>第7周学习总结 教材学习内容总结 第十三章 时间与日期 13.1.1时间的度量 1.格林威治时间(GMT):参考太阳到达最高点,有时间误差. 2 ...