Lisp代码的缩进

时间:2015-04-14 21:49:55

标签: 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)
  (cadr card))

(defun count-suit (suit hand)
  (length (remove-if-not #'(lambda (card) (equal suit (suit card))) hand)))

我对全局变量以及函数ranksuit很好,但是count-suit呢?我应该如何包裹它的身体并缩进呢?我可以想到几种方法,但不能决定什么是正确的。

任何提示?

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

3 个答案:

答案 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))

以上是我在这种情况下所写的内容。代码结构足够清晰,不会浪费太多空间。

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

如果我们看一下表达式,我们也希望以不同的方式编写它,因为它触发了很多通常不喜欢的东西:

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

所以:

(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以获取更多指导。