Как вывести функцию segs? - PullRequest
3 голосов
/ 10 января 2012

Как я могу построить функцию segs, которая возвращает список всех смежных сегментов в списке?Например, (segs '(l i s t)) должен дать следующий ответ:

(() (t) (s) (s t) (i) (i s) (i s t) (l) (l i) (l i s) (l i s t))

Меня особенно интересует, как решить эту проблему в соответствии с принципами проектирования, описанными в HtDP (нет, это не проблема изкнига, поэтому, пожалуйста, не стесняйтесь обсуждать это!) Как ее решить?Какие принципы использовать при выводе программ?

Ответы [ 2 ]

6 голосов
/ 10 января 2012

Начните с создания набора связанных примеров, наиболее тривиальных сначала:

(equal? (segs '())
        (list '()))
(equal? (segs '(z))
        (list '()
              '(z)))
(equal? (segs '(y z))
        (list '() '(z)
              '(y) '(y z)))
(equal? (segs '(x y z))
        (list '() '(z) '(y) '(y z)
              '(x) '(x y) '(x y z)))

Глядя на примеры, вы можете сделать наблюдение (которое я использовал для форматирования, чтобы выделить): ответдля каждого примера включены все элементы из ответа для предыдущего примера.Фактически, смежные подпоследовательности непустого списка - это просто смежные подпоследовательности его хвоста вместе с непустыми префиксами самого списка.

Так что отложите основную функцию и напишите non-empty-prefixes

non-empty-prefixes : list -> (listof non-empty-list)

С помощью этой вспомогательной функции легко написать основную функцию.

(Необязательно) Полученная наивная функция имеет плохую сложность, потому что она повторяет вызовыnon-empty-prefixes.Рассмотрим (segs (cons head tail)).Он вызывает (non-empty-prefixes tail) дважды: один раз, потому что он вызывает (segs tail), который вызывает (non-empty-prefixes tail), и снова, потому что он вызывает (non-empty-prefixes (cons head tail)), который вызывает (non-empty-prefixes tail) рекурсивно.Это означает, что наивная функция имеет излишне плохую сложность.

Проблема в том, что (segs tail) вычисляет (non-empty-prefixes tail), а затем забывает об этом, поэтому (segs (cons head tail)) должен повторить работу.Решение состоит в том, чтобы сохранить эту дополнительную информацию путем объединения segs и non-empty-prefixes в одну функцию, которая вычисляет оба ответа:

segs+ne-prefixes : list -> (values (listof list) (listof non-empty-list))

Затем определите segs как функцию адаптера, которая просто отбрасываетвторая часть.Это решает основную проблему со сложностью.

(отредактировано для добавления) Относительно segs+ne-prefixes: вот один из способов определения non-empty-prefixes.(Примечание: пустой список не имеет непустых префиксов. Не нужно выдавать ошибку.)

;; non-empty-prefixes : list -> (listof non-empty-list)
(define (non-empty-prefixes lst)
  (cond [(empty? lst)
         empty]
        [(cons? lst)
         (map (lambda (p) (cons (first lst) p))
              (cons '() (non-empty-prefixes (rest lst))))]))

И segs выглядит следующим образом:

;; segs : list -> (listof list)
(define (segs lst)
  (cond [(empty? lst) (list '())]
        [(cons? lst)
         (append (segs (rest lst))
                 (non-empty-prefixes lst))]))

Вы можете плавитьони вот так:

;; segs+ne-prefixes : list -> (values (listof list) (listof non-empty-list))
;; Return both the contiguous subsequences and the non-empty prefixes of lst
(define (segs+ne-prefixes lst)
   (cond [(empty? lst)
          ;; Just give the base cases of each function, together
          (values (list '())
                  empty)]
         [(cons? lst)
          (let-values ([(segs-of-rest ne-prefixes-of-rest)
                        ;; Do the recursion on combined function once!
                        (segs+ne-prefixes (rest lst))])
            (let ([ne-prefixes
                   ;; Here's the body of the non-empty-prefixes function
                   ;; (the cons? case)
                   (map (lambda (p) (cons (first lst) p))
                        (cons '() ne-prefixes-of-rest))])
              (values (append segs-of-rest ne-prefixes)
                      ne-prefixes)))]))

Эта функция по-прежнему следует рецепту проектирования (или она была бы, если бы я показывал свои тесты): в частности, она использует шаблон для структурной рекурсии в списке.HtDP не говорит о values и let-values, но вы можете сделать то же самое со вспомогательной структурой для группировки информации.

HtDP немного говорит о сложности, но такая перегруппировкавычисления обычно обсуждаются в курсе алгоритмов, в разделе «Динамическое программирование и запоминание».Обратите внимание, что альтернативой слиянию двух функций было бы запоминание non-empty-prefixes;это также исправило бы сложность.

И последнее: аргументы для append ближе к концу следует поменять местами до (append ne-prefixes segs-of-rest).(Конечно, это означает, что нужно переписать все тесты, чтобы использовать новый порядок, или написать / найти функцию сравнения списков, нечувствительных к порядку.) Попробуйте сравнить две версии функции в большом списке (около 300-400 элементов),посмотрите, сможете ли вы сказать разницу, и посмотрите, сможете ли вы это объяснить(Это скорее материал алгоритмов, а не дизайн.)

0 голосов
/ 10 января 2012

Происходит 2 рекурсии: первая отсекает атомы слева, а вторая - справа. Вот как я могу решить это рекурсивно в 2-х функциях, в простом выражении (так как я не владею схемой):

Start with the FullList variable in function A, 
accumulate right-chop results on FullList from recursive function B, 
then chop off the left character of FullList and call function A with it. 
Stop when an empty list is received. 

Суммируй все результаты, и ты золотой.

...