Lisp代码的缩进

Lisp代码的缩进,lisp,common-lisp,Lisp,Common Lisp,我已经写了一些Lisp代码,它可以工作,但我不知道如何正确地缩进它 基本上我有一个全局变量和三个函数: (setf my-hand '((3 hearts) (5 clubs) (2 diamonds) (4 diamonds) (ace spades))) (defun rank (card) (car card)) (defun suit (card)

我已经写了一些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
呢?我应该如何包装它的身体和缩进它?我可以想出几种方法,但不能决定什么是正确的

有什么提示吗

有没有一种规范的方法可以做到这一点?

我会用这个:

(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
不是包含表单的主体。阅读更多指南。

克里斯的回答涉及缩进,但还有其他几点。首先,setf不声明全局变量。为此,您需要defvardefparameter,并且您应该遵循“耳罩”惯例:

(defparameter *my-hand*
  '((3 hearts)
    (5 clubs)
    (2 diamonds)
    (4 diamonds)
    (ace spades)))
通过使用关键字参数来删除,您实际上可以删除带有计数套装的一些缩进问题。在这种情况下,您希望从手上移除具有不同套装的卡。这意味着您可以使用suit调用remove,对测试进行否定比较,并使用键函数从每张卡中获取套装:

(defun count-suit (suit hand)
  (length (remove suit hand
                  :key #'suit
                  :test (complement #'eql)))) ; or `:test-not #'eql`

但即使这样也比它需要的更详细,因为Common Lisp已经提供了一个count函数,该函数也有一个键参数,因此您可以执行以下操作:

(defun count-suit (suit hand)
  (count suit hand :key #'suit))

此外,对于访问器,您可能有兴趣使用defstruct来定义它们。您可以告诉defstruct使用列表作为其基础表示形式。这意味着您可以:

(defstruct (card (:type list))
  rank
  suit)


请注意,缩进和格式化之间有细微的区别

缩进通常意味着水平移动行的内容。通常,我们已经知道什么是在线和在线之前。如果您要求一个典型的编辑器缩进,它只会调整行内容的水平位置。它不会将表达式分布到行上

格式化意味着在一行或多行上布局代码。Lisp为自动布局提供了一个漂亮的打印机。但是在编辑器中,完整布局并没有得到很好的支持,特别是因为规则可能很复杂,并且很难处理注释和其他非s表达式代码内容。宏的布局基于简单的原则。对更复杂的宏(如循环)进行自动布局将非常困难

你的问题实际上是关于格式的

我可以识别函数调用吗?论点是什么?什么是语法?线路长度呢?压痕深度

让我们看看函数调用:将它们放在更显著的位置:

(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))
以上是我在本例中要写的内容。代码结构足够清晰,不会浪费太多空间

格式化代码意味着应用大量本地和全局约束/规则

如果我们看一下这个表达式,我们也会想用不同的方式来编写它,因为它触发了很多人们通常不喜欢的事情:

  • 它计数,但不使用计数功能
  • 它做不必要的工作
  • 它创建一个lambda表达式,用于提取一个值并针对一个项进行测试:extract和test
因此:

或者只是(
eql
是默认值):

回到格式化。我们可以做一些实验,看看Lisp是如何做到的,因为它内置了一个代码格式化程序(这里是Clozure Common Lisp):


尽管手动格式化的代码在许多情况下看起来更好,但熟悉自动格式化(也称为漂亮的打印或“研磨”)并能够处理它是很有用的。

谢谢您的回答。只有一个问题:你所说的“包含主体的”表单是什么意思?
defun
let
lambda
,等等。在代码主体中有一个部分。@GoloRoden但也要注意,你可以用一种不需要太多代码的方式更简单地定义计数套装:
(计数套装:key#suit)
。我还不知道
计数
,但当然你是对的,这是一种更干净的方法。setf不声明全局变量。事实上,setf对未声明变量的行为是未定义的行为。使用defvar或defparameter声明全局变量。感谢您指出这一点!我会注意这一点:-)是的,这是一个常见的陷阱,很多教程等都会弄错。为了简化代码,您还可以做其他一些事情,我已经在一篇文章中讨论过了。(Chris的最直接的是缩进。我的只是为了避免需要这么多代码。)非常感谢这个详细的答案:-)感谢非常详细的解释和自动格式化的提示。同时也感谢您指出缩进与格式化的关系:-)
(card-rank '(ace spaces))
;;=> ace
(card-suit '(5 clubs))
;;=> clubs
(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))
(length (remove-if-not #'(lambda (card)
                           (equal suit (suit card)))
                       hand))
(count suit hand :key #'suit :test #'eql)
(count suit hand :key #'suit)
? (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))