Функция groupSum схемы

Я пытаюсь написать функцию Scheme, которая принимает два параметра, список чисел и другое число, и возвращает true, если подмножество списка составляет это число.

Пример вывода:

> (groupSum '(1 2 5) 7)
> #t
> (groupSum '(1 2 5) 4)
> f

Пока все, что я могу сделать, это получить сумму всех чисел в списке. Что мне нужно сделать с моим кодом, чтобы получить вывод примера?

Вот мой код:

(define (groupSum elemList)
  (if
    (null? elemList)
    0
    (+ (car elemList) (groupSum (cdr elemList)))
  ))

Что возвращает мой код:

> (groupSum '(1 2 5))
> 8

person Bryce S    schedule 22.04.2018    source источник


Ответы (1)


Итак, вы спрашиваете, существует ли подмножество данного списка (в позиционном смысле, т.е. сохранение дубликатов, если таковые имеются), которое в сумме дает заданное число.

Прохождение подмножеств — это работа функции powerset, за исключением того, что мы хотим выйти из нее и вернуть #t, как только обнаружим успех. В противном случае просто верните #f в конце.

Дополнение функции powerset из одного из моих других ответов на этом сайте мы получаем

(define (group-adds-to aL aN)
  (call/cc
   (lambda (......)  ;; 2.
     (letrec 
        ([...... (lambda (aL)  ;; 4.
           (cond
             [(empty? aL) (list empty)]
             [else
              (add-into   ;; 5b.
                        (powerset (rest aL))  ;; 3b.
                        (first aL))]))]

         [...... (lambda (r a)  ;; 6.       ; `r` is recursive result, `a` an element
           (cond
             [(empty? r) empty]             ; nothing to add `a` to
             [else
              (cons (add a (first r)) ;; 7. ; either add `a`,
                    (cons (first r)         ;   or not, to the first in `r`
                          (add-into  ;; 5a. ; and recursively proceed
                           (rest r)         ;   to add `a` into the rest of `r`
                           a )))]))]

         [......                      ;; 8.
                  (lambda (......)       ;; 9...
                     (let ([sum ......])   ;; 10...
                       (if (= sum aN)  ;; 11.
                           (return #t)  ;; 1.
                           sum)))])

       (powerset aL)  ;; 3a.
       #f))))   ;; 12.

Заполнить бланки!

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

> (group-adds-to '(1 2 5) 7)
#t
> (group-adds-to '(1 2 5) 4)
#f

Требуется несколько тривиальных изменений, чтобы сделать этот #r5rs совместимым, если вам это нужно.

Конечно, этот код можно упростить и сократить, используя больше встроенных функций. Здесь тоже довольно просто избавиться от call/cc. Вы также можете проявить фантазию и добавить проверки для сокращения промежуточных списков, обнаруживая и удаляя такие подгруппы (подмножества), которые никак не могут способствовать успешному результату.


Хорошо, как заполнить пробелы.

  1. return? это что?
  2. это продолжение побега, созданное для нас call/cc. Когда (if) она вызывается как ;; 1., значение #t сразу же возвращается как окончательный результат всего вызова функции group-adds-to, независимо от того, насколько глубоко внутри рекурсии мы находимся в этот момент. Рекурсия просто прекращается, а результат просто возвращается.
    И если return никогда не вызывался, потому что равенство (= sum aN) ;; 11. так и не было обнаружено, рекурсия завершит свой ход, и #f будет возвращено нормально , как последнее выражение в теле функции, по адресу ;; 12..
  3. а,б powerset? что это?
  4. это внутренняя функция, которую мы определили
  5. а,б add-into?
  6. это тоже определено здесь.
  7. и add?
  8. Ага.
  9. Сделай сам. Посмотрите, как он используется, в ;; 7, каковы его аргументы, что он должен возвращать.
  10. Сделай сам. Ты можешь это сделать!
person Will Ness    schedule 22.04.2018
comment
Спасибо за вашу помощь! Я новичок в Схеме, поэтому я все еще не очень понимаю. Что я должен делать в пробелах? - person Bryce S; 22.04.2018
comment
внимательно прочитайте код и вставьте туда то, чего не хватает. :) Если бы вы знали Scheme, это было бы достаточно просто. Если вы этого не сделаете, SO - это не то место, где вас могли бы этому научить, извините. если есть конкретный вопрос, задавайте. :) - person Will Ness; 22.04.2018
comment
но сначала, просто чтобы я сориентировался, вы понимаете, какое отношение эта штука с набором мощности имеет к вашей проблеме? - person Will Ness; 22.04.2018
comment
Да, «powerset» позволяет вам брать подмножества заданного набора? Именно это я и пытаюсь сделать для этой задачи, чтобы проверить все возможные суммы чисел в наборе. - person Bryce S; 22.04.2018
comment
точно. и вместо того, чтобы сохранять подмножества до сих пор, мы сохраняем их суммы... - person Will Ness; 22.04.2018
comment
Вам удалось заполнить пробелы? вы можете опубликовать свой код на каком-нибудь сайте, таком как codepad.org, если хотите, чтобы я взглянул, или вы можете опубликовать новый вопрос с ним здесь, в stackoverflow (если у вас есть еще вопросы по этому поводу). хорошим продолжением является попытка реализовать то, что предлагается в последнем абзаце, о раннем пропуске некоторых подгрупп, чтобы контролировать их количество. в противном случае это может быть довольно тяжело с n! Всего подгрупп, потенциально. - person Will Ness; 23.04.2018
comment
Да! Я смог заставить его работать, спасибо за вашу помощь! - person Bryce S; 24.04.2018
comment
отличный! Вам удалось избавиться от call/cc и уменьшить количество промежуточных списков? требуется только небольшая настройка add-into для обоих. - person Will Ness; 24.04.2018
comment
Нет, я не смог. У меня не было времени на дальнейшую оптимизацию функции после того, как она заработала. - person Bryce S; 25.04.2018
comment
если у вас когда-нибудь будет время, вот подсказка: вы замените (return #t) в add только на #t, а add-into замените на (lambda (r a) (cond [(eq? #t r) #t] ...)) и внесите дополнительные изменения, чтобы все совпало. Таким образом, вместо consing при достижении первого #t он просто возвращается прямо вверх по цепочке вызовов. Это обычный способ добиться этого эффекта, пока вы еще учитесь. если это для теста, любое использование call/cc является сильным признаком того, что вы не написали код самостоятельно. call/cc считается расширенной функцией. - person Will Ness; 26.04.2018