Схема - Подмножества списка - PullRequest
3 голосов
/ 23 ноября 2011

Мне нужно написать функцию, которая будет производить все подмножества данного списка. У меня есть рекурсивная версия, которая использует карту, но в качестве бонуса меня просят создать функцию, которая делает это без использования явной рекурсии, локальной или каких-либо абстрактных функций списка. Мне разрешено использовать cons, empty?, empty, first, rest и cond. Я нахожусь на грани краха - какие-либо предложения? Должен ли я использовать лямбда-оператор для каждой нужной рекурсии?

Ответы [ 3 ]

2 голосов
/ 13 декабря 2011

Я очень сомневаюсь, что ваш профессор просит вас создать или использовать Y-Combinator для решения этой проблемы, но если вы хотите попробовать сделать это таким образом, вот некоторый код, который может помочь.С точки зрения непрофессионалов, y-комбинатор - это способ создания функции без необходимости что-либо определять, используя мощь лямбда-исчисления.Если у вас есть рабочее определение (которое вы упомянули, что вы делаете), то преобразовать его в лямбда-выражения не так уж сложно.Я пройдусь по шагам и постараюсь объяснить это ниже, но это очень сложная концепция и одна из самых «умопомрачительных» идей в функциональном программировании.

Обычно это следующееопределенная функция, которая будет возвращать длину данного списка:

;; mylength : [listof Any] -> Number
;; returns the length of xs
(define (mylength xs)
  (if (empty? xs)
      0
      (+ 1 (mylength (rest xs)))))

Теперь, чтобы начать выходить из стандартной области определений и явной рекурсии, давайте сделаем mylength лямбда-выражение, которое для данной функциикоторый способен определить длину списка, возвращает длину данного списка, ys.

;; ys is a [Listof Any]
;; mylength is a function that returns the length of a list
(λ (ys)
  (cond [(empty? ys) 0]
        [else (+ 1 (mylength (rest ys)))]))

Проблема с этим кодом в том, что мы не хотим иметь mylength в нашем коде вообще.Чтобы правильно мыслить, помните, что весь смысл этой лямбда-функции - возвращать длину заданного списка .Пока это будет выглядеть загадочным сообщением, но вы поймете, что я имею в виду, в нескольких строках.

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

;; ys is a [Listof Any]
(λ (len ys)
  ;; if ys is empty, return 0.
  (if (empty? ys) 0
      ;; otherwise, call len again, passing len itself as it's 1st argument.
      (+ 1 (len len (rest ys)))))

Вероятно, сбивать эту строку с толкуконец кода там: (+ 1 (len len (rest ys))))) В конце концов, len - это функция, которая просто берет список и возвращает его длину, верно? НЕПРАВИЛЬНО. У нас нет функции, которая принимает список и возвращает его длину - мы не можем использовать функцию, подобную первой mylength функции, упомянутой здесь.Нам нужна другая функция, цель которой - вернуть длину списка.Если вы помните, что я «загадочно» сказал несколько строчек вверх,

помните, что весь смысл этой лямбда-функции состоит в том, чтобы возвращать длину заданного списка

Итак, если эта лямбда-функция у нас возвращает длину заданного списка, а функция, которая нам нужна, возвращает длину заданного списка ... воу.Вы думаете, что я думаю?

((λ (len ys) (if (empty? ys) 0 
                  (+ 1 (len len (rest ys)))))
  (λ (len xs) (if (empty? xs) 0
                  (+ 1 (len len (rest xs))))) '(your list here))

"Что !?"Вы, вероятно, думаете: «Вы можете сделать это !?»

Ответ, друг мой, да.Да, ты можешь.Если вы заблудились, внимательно, внимательно, внимательно посмотрите на приведенный выше код.Давайте назовем внешнюю лямбда-функцию func 1, а внутреннюю лямбда-функцию func 2.func 1 - это та же функция, которую мы писали ранее.Эта часть имеет смысл, верно?func 1 принимает некоторую другую функцию len и список ys и пытается вернуть длину ys.Поскольку цель len в func 1 является той же самой целью , которую имеет func 1, мы можем передать то, что по сути func 1, в качестве аргумента len внешней лямбды.

Чтобы понять это немного лучше, давайте перейдем в список '(1) к нашему новому, странному лямбда-монстру:

Первый шаг:

(empty? '(1)) -> FALSE

внешняя лямбда определяет, что '(1) не пусто, поэтому он оценивает следующий шаг.

`(+ 1 (len len (rest '(1))))

(rest '(1)) оценивается как empty:

(+ 1 (len len empty))

, а len равно

(λ (len xs) (if (empty? xs) 0 (+ 1 (len len (rest xs))))), поэтому приведенное выше значение расширяется до:

((λ (len ys) (if (empty? ys) 0 
                  (+ 1 (len len (rest ys)))))
  (λ (len xs) (if (empty? xs) 0
                  (+ 1 (len len xs)))) empty)

, а затем empty проталкивается через внешнюю лямбду как ys, и первый условный оператор оценивается:

(empty? ys)

(empty? empty) -> TRUE

Таким образом, вызов (len len empty) возвращает 0.Теперь он переходит к следующему шагу:

(+ 1 0)', which evaluates to 1 , which is the length of the list '(1) `.

Это очень сложная для понимания концепция, и я не думаю, что проделал очень хорошую работу по объяснению этой концепции, поскольку помог вам пройти через все шаги, необходимые для ее идентификации. Если вы поймете, что понимаете, как работает Y-Combinator, я буду достаточно впечатлен - как вашей способностью понимать мои проблемы, так и моей способностью объяснить такую ​​запутанную концепцию.

 1. (empty? '(1)) -> FALSE
 2. (+ 1 (len len (rest ys)))

((λ (len ys) (if (empty? '(1)) 0 
                  (+ 1 (len len (rest ys)))))
  (λ (len xs) (if (empty? xs) 0
                  (+ 1 (len len (rest xs))))) '(your list here))
0 голосов
/ 23 ноября 2011

Судя по вашему ответу, идея здесь состоит в том, чтобы использовать одну из функций 'fold', а не делать рекурсивные вызовы в вашем коде.Если вы достаточно знакомы с «сбросить», это не невозможно.По сути, вам нужно смоделировать свой процесс, подумав сначала о том, какую информацию нужно вычислять на каждом этапе процесса, а затем о том, как объединить новую информацию со старой информацией.

Я думаю, что вам лучше всего записать это:

(define (all-subsets l) (foldl ..?.. ..?.. l))

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

0 голосов
/ 23 ноября 2011

Хм, делать это без какой-либо рекурсии звучит так, как будто это будет довольно сложно.Вам разрешат использовать letrec, как описано здесь?http://docs.racket -lang.org / reference / let.html # (form ._ ((lib._racket / private / lettx-схема..rkt) ._ letrec))

Этомог бы сделать это для вас, если это разрешено, но в остальном я не уверен.

...