我已经编写了一些Lisp代码,但它确实有效,但我不确定如何正确地缩进它。
基本上我有一个全局变量和三个函数:
(setf my-hand '((3 hearts)
(5 clubs)
(2 diamonds)
(4 diamonds)
(ace spades)))
(defun rank (card)
(car card))
(defun suit (card)
(cadr card))
(defun count-suit (suit hand)
(length (remove-if-not #'(lambda (card) (equal suit (suit card))) hand)))
我对全局变量以及函数rank
和suit
很好,但是count-suit
呢?我应该如何包裹它的身体并缩进呢?我可以想到几种方法,但不能决定什么是正确的。
任何提示?
有一种规范的方法可以做到这一点吗?
答案 0 :(得分:6)
请注意,缩进和格式之间存在细微差别。
缩进通常意味着水平移动线条的内容。通常我们已经知道了之前的线和线。如果你要求一个典型的编辑器缩进,它只会调整行内容的水平位置。它不会在线上分发表达式。
格式化表示在一行或多行上布局代码。 Lisp为自动布局提供了漂亮的打印机。但是在编辑器中,完全布局并不是很受支持,特别是因为规则可能很复杂,并且很难处理注释和其他非s表达式代码内容。宏的布局基于简单的原则。像LOOP这样的更复杂的宏的自动布局将非常困难。
您的问题实际上是关于格式化。
(length (remove-if-not #'(lambda (card) (equal suit (suit card))) hand))
我可以识别函数调用吗?有什么争论?什么是语法?线长度怎么样?压痕深度?
让我们看一下函数调用:将它们更突出地放置:
(length
(remove-if-not
#'(lambda (card)
(equal
suit
(suit card)))
hand))
以上并不是那么糟糕。
也许我们希望专注于参数并确保两个或多个参数分开:
(length (remove-if-not #'(lambda (card)
(equal suit
(suit card)))
hand))
通常我们希望简短的arglists在一行上,如果行不太长:
(length (remove-if-not #'(lambda (card)
(equal suit (suit card)))
hand))
以上是我在这种情况下所写的内容。代码结构足够清晰,不会浪费太多空间。
格式化代码意味着应用许多本地和全局约束/规则。
如果我们看一下表达式,我们也希望以不同的方式编写它,因为它触发了很多通常不喜欢的东西:
所以:
(count suit hand :key #'suit :test #'eql)
或只是(eql
是默认值):
(count suit hand :key #'suit)
返回格式化。我们可以做一些实验,看看Lisp是如何做到的,因为它内置了一个代码格式化程序(这里是Clozure Common Lisp):
? (defun test ()
(dolist (*print-right-margin* '(80 60 40 30))
(format t "~%Margin: ~a" *print-right-margin*)
(pprint '(length (remove-if-not #'(lambda (card) (equal suit (suit card))) hand)))))
TEST
? (test)
Margin: 80
(LENGTH (REMOVE-IF-NOT #'(LAMBDA (CARD) (EQUAL SUIT (SUIT CARD))) HAND))
Margin: 60
(LENGTH (REMOVE-IF-NOT
#'(LAMBDA (CARD) (EQUAL SUIT (SUIT CARD)))
HAND))
Margin: 40
(LENGTH
(REMOVE-IF-NOT
#'(LAMBDA
(CARD)
(EQUAL SUIT (SUIT CARD)))
HAND))
Margin: 30
(LENGTH
(REMOVE-IF-NOT
#'(LAMBDA
(CARD)
(EQUAL
SUIT
(SUIT CARD)))
HAND))
即使手动格式化的代码在许多情况下可能看起来更好,但熟悉自动格式化(也就是漂亮的打印或研磨')以及能够处理它是很有用的。
答案 1 :(得分:5)
克里斯'回答地址缩进,但也有一些其他要点。首先, setf 不会声明全局变量。为此你需要 defvar 或 defparameter ,你应该遵循"耳罩"约定:
(defparameter *my-hand*
'((3 hearts)
(5 clubs)
(2 diamonds)
(4 diamonds)
(ace spades)))
您可以使用删除的关键字参数,通过 count-suit 删除一些缩进问题。在这种情况下,您需要从手中删除带有不同套装的卡片。这意味着您可以使用诉讼调用删除,并对测试进行否定比较,并使用关键功能从每张卡中获取诉讼:
(defun count-suit (suit hand)
(length (remove suit hand
:key #'suit
:test (complement #'eql)))) ; or `:test-not #'eql`
(count-suit 'diamonds *my-hand*)
;;=> 2
但是,即使这比它需要的还要冗长,因为Common Lisp已经提供了一个 count 函数,它也有一个关键参数,所以你可以这样做:
(defun count-suit (suit hand)
(count suit hand :key #'suit))
(count-suit 'hearts *my-hand*)
;;=> 1
此外,关于访问者,您可能有兴趣使用 defstruct 来定义它们。您可以告诉 defstruct 使用列表作为其基础表示。这意味着您可以:
(defstruct (card (:type list))
rank
suit)
(make-card :rank 3 :suit 'hearts)
;;=> (3 hearts)
(card-rank '(ace spaces))
;;=> ace
(card-suit '(5 clubs))
;;=> clubs
答案 2 :(得分:2)
我会用这个:
(defun count-suit (suit hand)
(length (remove-if-not (lambda (card)
(equal suit (suit card)))
hand)))
或者,这也没关系:
(defun count-suit (suit hand)
(length
(remove-if-not (lambda (card)
(equal suit (suit card)))
hand)))
请注意,remove-if-not
只是从length
缩进的一个空格,因为length
不是包含正文的表单。请阅读Riastradh's Lisp Style Rules以获取更多指导。