2015-04-14 4 views
0

Я написал некоторый код Лиспа, и он работает, но я не уверен, как правильно его отступать.Отступ кода Лиспа

В принципе у меня есть глобальная переменная и три функции:

(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? Как я должен обернуть его тело и отступом? Я могу думать о нескольких путях, но не могу решить, что кажется правильным.

Любые подсказки?

Есть ли канонический способ, как это сделать?

+4

** setf ** не объявляет глобальные переменные. На самом деле поведение ** setf ** на необъявленных переменных является неопределенным поведением. Объявлять глобальные переменные с ** defvar ** или ** defparameter **. –

+0

Спасибо, что указали это! Я буду следить за этим :-) –

+2

Да, это обычная ловушка, и много уроков и т. Д., Ошибаются. Есть еще кое-что, что вы можете сделать, чтобы упростить свой код, который я обсуждал в [дополнительном ответе] (http://stackoverflow.com/a/29638261/1281433). (Тем не менее, Крис больше всего относится к отступу. Mine просто избегает необходимости в таком количестве кода.) –

ответ

5

Обратите внимание, что есть небольшая разница между отступов и форматирование.

Отступ обычно означает перемещение содержимого строки по горизонтали. Обычно мы уже знаем, что находится на линии и линии раньше. Если вы попросите типичный редактор указать подпункт, он не будет ничего, кроме как настроить горизонтальное положение содержимого линии. Он не будет распространять выражения по строкам.

Форматирование означает компоновку кода над одной или несколькими линиями. Lisp предоставляет красивый принтер для автоматической компоновки. Но в редакторе полный макет не очень хорошо поддерживается, тем более, что правила могут быть сложными, и довольно сложно обрабатывать комментарии и другое содержимое кода, отличного от s-expression. Макет макросов основан на простых принципах. Автоматическая компоновка для более сложных макросов, таких как 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) 

Вернуться к форматированию. Мы можем сделать несколько экспериментов и посмотреть, как Лисп делает это, так как он имеет код форматировщик встроенный в (здесь 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)) 

Даже если вручную отформатированный код может выглядеть лучше во многих случаях, это полезно познакомиться с автоматическим форматированием (ака довольно печатной или «шлифовальной») и иметь возможность справиться с этим.

+0

Спасибо за очень подробное объяснение и подсказку с автоматическим форматированием. И спасибо также за то, что он указал на форматирование отступов :-) –

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 для получения дополнительной информации.

+0

Спасибо за ваш ответ. Только один вопрос: что вы подразумеваете под формой, содержащей тело? –

+2

'defun',' let', 'lambda' и т. Д. Имеют раздел для тела кода. –

+0

Хорошо, спасибо :-) –

5

Ответы Криса на адрес, но есть и другие моменты. Во-первых, setf не Объявление глобальных переменных. Для этого вам необходим DefVar или defparameter, и вы должны следовать «наушник» соглашению:

(defparameter *my-hand* 
    '((3 hearts) 
    (5 clubs) 
    (2 diamonds) 
    (4 diamonds) 
    (ace spades))) 

Вы действительно можете удалить некоторые из вопросов, отступов с счетом костюмом используя аргументы для ключевых слов удалить. В этом случае вы хотите удалить карты с помощью различных костюмов. Это означает, что вы можете вызвать удалить с костюмом с инвертированным сравнением для теста, и с помощью ключа функции, чтобы получить костюм от каждой карты:

(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 уже предоставляет функцию , которая также имеет ключевой аргумент, так что вы можете сделать это:

(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 
+1

Большое спасибо за этот подробный ответ :-) –

Смежные вопросы