Сортировка списка, составленного из ручных структур в lisp

#functional-programming #lisp #common-lisp #clisp

#функциональное программирование #lisp #common-lisp #clisp

Вопрос:

У меня есть структура в моем коде

 (defstruct tree-node char freq )
  

И у меня есть список этих «узлов». Например (# a4, #b5, #q17), и я хочу отсортировать их в порядке убывания. Как я могу использовать функцию сортировки. Я написал функцию сравнения, но я не знаю, можно ли это сделать.

 (defun compare()( if( > (tree-node-freq tree-node-freq) T (nil))))
  

Когда я вызываю функцию сортировки

 (sort list compare)
  

в нем говорится, что функция сравнения не имеет значения. Кстати, я новичок в lisp, так что не судите, пожалуйста 🙂

Комментарии:

1. Вероятно, хорошая идея проверить документацию / примеры для СОРТИРОВКИ: lispworks.com/documentation/HyperSpec/Body/f_sort_.htm#sort

Ответ №1:

sort Функция ожидает функцию-предикат, которая принимает два аргумента и возвращает обобщенное логическое значение, в то время как операционный код показывает comparison функцию, которая не принимает аргументов. Нет причин использовать if , когда само сравнение возвращает желаемое логическое значение. Также обратите внимание, что для доступа к функции в sort выражении требуется кавычка, т. Е. (sort list #'compare) Вместо (sort list compare) .

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

 CL-USER> (defun node-greater-p (n1 n2)
           (> (tree-node-freq n1) (tree-node-freq n2)))
NODE-GREATER-P

CL-USER> *nodes*
(#S(TREE-NODE :CHAR #a :FREQ 4) #S(TREE-NODE :CHAR #b :FREQ 5)
 #S(TREE-NODE :CHAR #q :FREQ 17))

CL-USER> (sort *nodes* #'node-greater-p)
(#S(TREE-NODE :CHAR #q :FREQ 17) #S(TREE-NODE :CHAR #b :FREQ 5)
 #S(TREE-NODE :CHAR #a :FREQ 4))
  

С тем же успехом вы могли бы сделать это с помощью анонимной функции:

 CL-USER> *nodes*
(#S(TREE-NODE :CHAR #a :FREQ 4) #S(TREE-NODE :CHAR #b :FREQ 5)
 #S(TREE-NODE :CHAR #q :FREQ 17))

CL-USER> (sort *nodes* #'(lambda (n1 n2) (> (tree-node-freq n1) (tree-node-freq n2))))

(#S(TREE-NODE :CHAR #q :FREQ 17) #S(TREE-NODE :CHAR #b :FREQ 5)
 #S(TREE-NODE :CHAR #a :FREQ 4))
  

Но было бы еще лучше воспользоваться :key аргументом sort функции, которая предоставляет ключ сортировки:

 CL-USER> *nodes*
(#S(TREE-NODE :CHAR #a :FREQ 4) #S(TREE-NODE :CHAR #b :FREQ 5)
 #S(TREE-NODE :CHAR #q :FREQ 17))

CL-USER> (sort *nodes* #'> :key #'tree-node-freq)

(#S(TREE-NODE :CHAR #q :FREQ 17) #S(TREE-NODE :CHAR #b :FREQ 5)
 #S(TREE-NODE :CHAR #a :FREQ 4))
  

Ответ №2:

Если вы определяете свою compare функцию в REPL, мы видим предупреждения:

 (defun compare()( if( > (tree-node-freq tree-node-freq) 
                               T
                               nil)))
; in: DEFUN COMPARE
;     (IF (> (SBCLI::TREE-NODE-FREQ SBCLI::TREE-NODE-FREQ) T NIL))
; 
; caught ERROR:
;   error while parsing arguments to special operator IF:
;     too few elements in
;       ((> (TREE-NODE-FREQ TREE-NODE-FREQ) T NIL))
;     to satisfy lambda list
;       (SB-C::TEST SB-C::THEN amp;OPTIONAL SB-C::ELSE):
;     between 2 and 3 expected, but got 1
; 
; compilation unit finished
;   caught 1 ERROR condition
COMPARE
  

Вы помещаете закрывающую скобку > слишком далеко. Вместо > a b этого мы получили только > (a) b c . Таким if образом, у then нет ни предложения, ни предложения else.

Возможно, вы захотите использовать редактор с отступом lisp.

Сравнить

Просмотр sort документации:

 SORT names a compiled function:
  Lambda-list: (SEQUENCE SB-IMPL::PREDICATE amp;REST SB-IMPL::ARGS amp;KEY
                SB-IMPL::KEY)
  Declared type: (FUNCTION
                  (SEQUENCE (OR FUNCTION SYMBOL) amp;REST T amp;KEY
                   (:KEY (OR FUNCTION SYMBOL)))
                  (VALUES SEQUENCE amp;OPTIONAL))
  Documentation:
    Destructively sort SEQUENCE. PREDICATE should return non-NIL if
       ARG1 is to precede ARG2.
  Inline proclamation: MAYBE-INLINE (inline expansion available)
  

В нем упоминаются два аргумента функции сравнения. И действительно, здесь у нас есть пример с лямбда-функцией: http://www.lispworks.com/documentation/HyperSpec/Body/f_sort_.htm

  (setq tester (copy-seq "lkjashd")) =>  "lkjashd"
 (stable-sort tester #'(lambda (x y) (and (oddp x) (evenp y))))
  

Ваша функция должна принимать два аргумента, tree-1 и tree-2 .

Сравните их с (> (tree-node-freq tree-1) (tree-node-freq tree-2)) .

Последние биты

 (nil)
  

Вы вызываете функцию «nil». Просто возвращайте nil , как вы возвращаетесь T .

А еще лучше, используйте when . Если значение when равно false , функция неявно возвращает nil .

 (sort list compare)
  

Вы должны ссылаться compare как на функцию : #'compare . См. https://lispcookbook.github.io/cl-cookbook/functions.html#referencing-functions-by-name-single-quote—or-sharpsign-quote-