Lisp代码的缩进

Gol*_*den 0 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)))
Run Code Online (Sandbox Code Playgroud)

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

任何提示?

是否有规范的方法来做到这一点?

Rai*_*wig 6

请注意,缩进格式之间存在细微差别.

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

格式化意味着在一行或多行上布局代码.Lisp为自动布局提供了漂亮的打印机.但是在编辑器中,完全布局并不是很受支持,特别是因为规则可能很复杂,并且很难处理注释和其他非s表达式代码内容.宏的布局基于简单的原则.像LOOP这样的更复杂的宏的自动布局将非常困难.

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

(length (remove-if-not #'(lambda (card) (equal suit (suit card))) hand))
Run Code Online (Sandbox Code Playgroud)

我可以识别函数调用吗?有什么争论?什么是语法?线长度怎么样?压痕深度?

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

(length
 (remove-if-not
  #'(lambda (card)
      (equal
       suit
       (suit card)))
  hand))
Run Code Online (Sandbox Code Playgroud)

上面看起来并不那么糟糕.

也许我们想要专注于参数并确保两个或多个参数在不同的行上:

(length (remove-if-not #'(lambda (card)
                           (equal suit
                                  (suit card)))
                       hand))
Run Code Online (Sandbox Code Playgroud)

通常我们希望短线arglists在一条线上,如果线不太长:

(length (remove-if-not #'(lambda (card)
                           (equal suit (suit card)))
                       hand))
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

所以:

(count suit hand :key #'suit :test #'eql)
Run Code Online (Sandbox Code Playgroud)

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

(count suit hand :key #'suit)
Run Code Online (Sandbox Code Playgroud)

回到格式化.我们可以做一些实验,看看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))
Run Code Online (Sandbox Code Playgroud)

即使手动格式化的代码在许多情况下可能看起来更好,但熟悉自动格式化(也就是漂亮的打印或"磨削")并能够处理它是很有用的.


Jos*_*lor 5

克里斯的回答解决了缩进问题,但也有其他几点.首先,setf声明全局变量.为此你需要defvardefparameter,你应该遵循"耳罩"惯例:

(defparameter *my-hand*
  '((3 hearts)
    (5 clubs)
    (2 diamonds)
    (4 diamonds)
    (ace spades)))
Run Code Online (Sandbox Code Playgroud)

实际上,您可以使用要删除的关键字参数来删除count-suit中的一些缩进问题.在这种情况下,您想要从手中移除具有不同套装的卡片.这意味着您可以使用套装调用remove,对测试进行否定比较,并使用键功能从每张卡中获取套装:

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

(count-suit 'diamonds *my-hand*)
;;=> 2
Run Code Online (Sandbox Code Playgroud)

但是,即使它比它需要的更冗长,因为Common Lisp已经提供了一个也有一个关键参数的计数函数,所以你可以这样做:

(defun count-suit (suit hand)
  (count suit hand :key #'suit))
Run Code Online (Sandbox Code Playgroud)

(count-suit 'hearts *my-hand*)
;;=> 1
Run Code Online (Sandbox Code Playgroud)

此外,关于访问者,您可能有兴趣使用defstruct来定义它们.您可以告诉defstruct使用列表作为其底层表示.这意味着您可以:

(defstruct (card (:type list))
  rank
  suit)
Run Code Online (Sandbox Code Playgroud)

(make-card :rank 3 :suit 'hearts)
;;=> (3 hearts)
Run Code Online (Sandbox Code Playgroud)

(card-rank '(ace spaces))
;;=> ace
Run Code Online (Sandbox Code Playgroud)

(card-suit '(5 clubs))
;;=> clubs
Run Code Online (Sandbox Code Playgroud)