проверить, является ли список целых чисел возрастающим

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

(: ascending : (Listof Integer) -> Boolean)
;; test whether a list of integers is *strictly* ascending
(define (ascending x)
  (match x
    ('() #f)
    ((cons hd tl)
     (cond
       ((< hd (second x)) #t)
       ((> hd (second x)) #f)
       (else (ascending tl)))))) 

Он работает для первых трех проверок, но не для последних трех, как указано ниже:

(check-expect (ascending (list 1 2 3 4 5)) #t)
(check-expect (ascending (list 5 4 3 2 1)) #f)
(check-expect (ascending (list 5 1 2 3 4)) #f)
(check-expect (ascending (list 1 2 3 5 4)) #f)
(check-expect (ascending (list 1 3 2 4 5)) #f)
(check-expect (ascending (list 1 2 3 4 1)) #f)

Я не понимаю, что я делаю неправильно, потому что я должен сделать следующее:

  1. Проверьте, не пуст ли список, и выдайте ошибку. Сделанный. Поставить галочку.
  2. Если в списке только два элемента, сравните hd и tl и если hdtl, то #t, если нет, то #f. Готово Отметьте.
  3. Другой выполняет два сравнения для всего списка, пока не будет получено значение #t или #f.

Пожалуйста помоги.


person testomg    schedule 09.11.2016    source источник


Ответы (5)


У вас есть несколько проблем.

Во-первых, вам не хватает случая для списка только с 1 элементом, который должен быть базовым случаем и возвращать #t. В противном случае вы будете использовать (second x) в списке только с 1 элементом, что является ошибкой.

Затем, когда в списке 2 или более элементов, он увеличивается, если первый элемент меньше второго элемента и конец списка также увеличивается. Вы проверяете только первые два элемента. Ваше предложение else выполняется только тогда, когда первый и второй элементы равны, потому что первые две проверки cond обрабатывают < и >. Рекурсивный вызов не является отдельным случаем, он объединен со случаем, когда первые два элемента возрастают.

(define (ascending x)
  (match x
    ('() #f) ;; Empty list is error
    ((cons hd '()) #t) ;; 1-element is success
    ((cons hd tl)
     (if (< hd (second x))
         (ascending tl)
         #f))))

if также можно упростить до:

(and (< hd (second x)) (ascending tl))
person Barmar    schedule 09.11.2016
comment
Большое спасибо, мистер Бармар! Я действительно ценю твою помощь. - person testomg; 10.11.2016

Поскольку функция #'< принимает любое количество аргументов, вы можете просто сделать:

(apply #'< '(1 2 3 4)) => T
person Leo    schedule 10.11.2016
comment
Этот вопрос касается Typed Racket, а не CL, но ваш ответ будет правильным, если вы просто удалите #' и замените T на #t. - person Alexis King; 10.11.2016

Если вам разрешено использовать сопоставление с шаблоном для решения этой проблемы, вы можете использовать более сложные шаблоны, чтобы разбить свою проблему на отдельные части.

(: ascending? : (Listof Integer) -> Boolean :)
(define (ascending? xs)
  (match xs
    [(list)           #f] ;; empty list is not considered ascending
    [(list a)         #t] ;; single-element list is always ascending
    ;; match first two elements (a, b) and remaining elements as c
    ;; a should be less than b, and recurse
    [(list a b c ...) (and (< a b) (ascending? (cons b c)))]))

Для меня это читается намного лучше, чем соответствие стиля '() или (cons ...). А поскольку в третьем случае мы использовали более выразительный шаблон, код, который на самом деле делает что-то, остается очень простым.

(ascending? '())               ;; => #f
(ascending? '(1))              ;; => #t
(ascending? '(1 2))            ;; => #t
(ascending? '(1 2 3 4 5 6 7))  ;; => #t
(ascending? '(1 2 3 1))        ;; => #f

Я должен упомянуть, что нет ничего плохого в другом стиле шаблонов, который вы использовали. Я просто думаю, что легче читать, когда шаблоны последовательны. Таким образом, вместо того, чтобы сопоставлять '() с cons и list и т. д., если вы хотите использовать стиль шаблонов в кавычках, используйте их для каждого случая, чтобы было легче увидеть, какие случаи вы обрабатываете.

Эта ascending? процедура работает так же, только использует другой стиль выражений сопоставления с образцом.

(: ascending? : (Listof Integer) -> Boolean :)
(define (ascending? xs)
  (match xs
    [`()               #f]
    [`(,a)             #t]
    [`(,a . (,b . ,c)) (and (< a b) (ascending? (cons b c)))]))

И еще: ты сказал...

Проверьте, не пуст ли список, и выдайте ошибку. Сделанный. Поставить галочку.

Возврат #f не вызывает ошибку. Если вы действительно намерены вызвать ошибку, если кто-то передает пустой список, вам следует использовать процедуру error

...
(match xs
  [(list) (error 'ascending? "empty list given")]
  ...
person Mulan    schedule 10.11.2016
comment
@benrugers, не связанные с этой процедурой. Похоже, ОП хочет вернуть #f на вопрос, находится ли этот список в порядке возрастания? когда и пустой список используется в качестве входных данных. Лично я считаю, что он должен возвращать #t, потому что пустой список не не в порядке возрастания. Последняя часть моего ответа также касается возможности выбрасывания и ошибки. - person Mulan; 11.11.2016

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

Технические характеристики

Например, можно сказать, что строго упорядоченный список имеет два математических свойства:

  1. Список отсортирован (монотонно).

  2. Длина списка равна мощности множества, содержащего все элементы списка.

Реализация

Работая с этой более математической спецификацией:

 #lang typed/racket
 (: strictly-ordered-list? : (Listof Integer) -> Boolean)
 (define (strictly-ordered-list? xs) 
   (define sorted (sort xs <))
     (and (equal? xs sorted)
       (= (set-count (list->set xs))
          (length sorted))))

Обсуждение

Реализация написана на интересующем нас уровне абстракции, списках. Он проверяет свойства списков и не увязает в переборе и сравнении элементов. Потенциально, он торгует немного скоростью, но обычно для данных интересного размера O (n log n) по сравнению с O (n) вряд ли будет узким местом.

person ben rudgers    schedule 10.11.2016
comment
Потенциально, это немного снижает скорость, но обычно для данных интересного размера O(n log n) по сравнению с O(n) вряд ли будет узким местом. Случаи, когда мы просто< /i> хотите проверить, отсортированы ли элементы, вместо того, чтобы сортировать их безоговорочно, это именно те, где сложность имеет значение. Обычно ожидается, что проверка будет более простой операцией, и здесь вы нарушаете это ожидание, поэтому она начинается с отрицательной оценки, даже если принять во внимание прагматическую точку зрения. Что мы получаем взамен? идея, выражающая высокий уровень... - person coredump; 10.11.2016
comment
... свойство достаточно для его реализации. Логическая формула, подходящая для доказательств или абстрактных рассуждений, плохо автоматически преобразуется в рабочие шаги. Несмотря на то, что вы не должны преждевременно оптимизировать, существует определенная экономия, которая ожидается от реализации, в частности, для таких основных вещей. См. также: accidentallyquadratic.tumblr.com - person coredump; 10.11.2016
comment
@coredump Я предвзято отношусь к профилированию реального приложения, а не к кабинетным теориям оптимизации; начать с чего-то правильного, а затем посмотреть, не нужно ли это сделать быстрее; и читаемый поддерживаемый код. Я часто стараюсь следовать совету этого человека: youtube.com/watch?v=4nhFqf_46ZQ. когда дело доходит до разработки программ. - person ben rudgers; 10.11.2016
comment
(1) Поскольку вы не определили сортировку, я могу предположить, что это Racket, который не удаляет идентичные элементы: ваше свойство set не будет сохраняться; и нигде в документации не гарантируется, что sort возвращает тот же список, когда он уже отсортирован. Это может быть копия. Правильный код не должен полагаться на такие детали реализации (вы используете eq?). (2) Почему быстрый код обязательно будет трудным для чтения? (loop for (a b) on list while b always (< a b)) в CL правильно и быстро. Профилирование — это здорово, но это не оправдание плохому дизайну. Лэмпорт не стал бы писать версию O(n.log n). - person coredump; 10.11.2016
comment
@coredump Изменено eq? на equal?, что подходит для пар. Спасибо, что указали на мою 12:00 ошибку реализации относительно свойств списков. Я не понимаю вашу точку зрения о наборах. Сравнение проводится между кардинальностью набора и количеством элементов в списке. Если значения отличаются, то список содержит дубликаты. Да, sort встроен в рэкет #lang, потому что он лучше протестирован, чем все, что я мог бы сделать сам. Любопытно, как цикл будет возвращать разные сообщения об ошибках, касающихся монтонности по сравнению с уникальными значениями. - person ben rudgers; 10.11.2016
comment
Мне интересно, потому что разные сообщения об ошибках могут быть полезны при таких действиях, как отладка, обработка различных входных данных и наблюдение за статистическими свойствами источника входных данных. - person ben rudgers; 10.11.2016
comment
Хорошо, я понимаю, что вы делаете с промежуточным набором, и стою исправленным. Благодарю за разъяснение. Что касается сообщений об ошибках, монотонность против уникальности... ну, предикат просто должен сказать нам, является ли список строго отсортированным или нет. Остальное реализация. То, что у вас есть два случая для рассмотрения, связано с тем, как вы смоделировали проблему. Цикл проверяет, что последовательные элементы строго возрастают, что достаточно для проверки нужного свойства в списке по транзитивности. - person coredump; 10.11.2016
comment
Если бы вы определили отсортированный список как конкатенацию двух отсортированных подсписков (где end1 ‹ start2), вам пришлось бы рассматривать еще больше случаев и потенциально сообщать об ошибках во всех этих ситуациях, но это не означает, что такой подход является желательным. - person coredump; 10.11.2016
comment
@coredump Работа со списками, поскольку абстракция облегчает написание предиката, где список monotonic /\ not strictly monotonic. Любой предикат, который может быть определен с помощью DFA, может быть определен синтаксическим анализатором, обратное неверно. Возможность записать этот конкретный предикат в виде цикла — это просто счастливая случайность в бизнес-логике. Это не обобщается на списки целых чисел. - person ben rudgers; 11.11.2016
comment
Замените < на <= или обобщите с помощью функции сравнения, взятой в качестве аргумента, если хотите. Функция сравнения определяет, является ли порядок строгим или нет, и, в свою очередь, является ли список строго или слабо монотонным. Это не счастливая случайность, это индуктивное определение, следующее за самим определением отсортированных списков. Я предпочитаю, чтобы мои итеративные индуктивные процессы записывались в виде циклов, вот и все. Я понимаю желание выразить только то, что вычисляется, не вдаваясь в подробности того, как это делается. Но разве цикл не выглядит почти как спецификация? - person coredump; 11.11.2016
comment
@coredump <= не определяет, соответствует ли длина списка кардинальности набора его элементов. Он соответствует как строго монотонным, так и не строго монотонным спискам. < соответствует строго монотонным спискам. DFA не может сопоставлять списки, которые monotonic /\ ~strictly monotonic. Цикл — это реализация, а не спецификация, это именно то, о чем говорит Лэмпорт и его забота о том, сколько программ будет написано. - person ben rudgers; 11.11.2016
comment
Рассмотрим только два значения, a и b. Вы говорите, что для проверки того, задано ли a ‹ b либо ‹, либо ‹= (обозначается R), вы проверяете, являются ли a R b и card({a, b}) = 2. Я не преследую той же цели. Я проверяю, является ли a R b, и пусть R определяет, является ли порядок строгим или нет. И (ascending list <), и (ascending list <=) служат разным целям. - person coredump; 11.11.2016

Можно также использовать итеративный цикл for с set!, хотя обычно это нежелательно:

(define (asc? l)
  (define res #t)
  (for ((i (sub1 (length l)))
        #:when (not(< (list-ref l i)
                      (list-ref l (add1 i)))))
    (set! res #f)
    )
  res)

Тестирование:

(asc? (list 1 2 3 4 5))

(asc? (list 1 2 3 4 5 4))

Выход:

#t
#f
person rnso    schedule 11.11.2016