Функция рекурсивной перестановки всегда возвращает пустой список - PullRequest
6 голосов
/ 08 июля 2011

Я изучаю Haskell, и одна из моих тренировочных функций была простой рекурсивной permute.Я адаптировал решение, описанное здесь и первоначально получил это:

selections [] = []
selections (x:xs) = (x, xs) : [ (y, x:ys) | (y,ys) <- selections xs ]

permute xs = [y:ps | (y,ys) <- selections xs, ps <- permute ys]

(Да, это может быть короче, но я собирался для ясности и ясности.)

Однако эта версия permute всегда возвращала пустой список!Немного поколебавшись, я заставил его работать, изменив permute на:

permute [] = [[]]
permute xs = [y:ps | (y,ys) <- selections xs, ps <- permute ys]

Однако, я все еще озадачен , почему исходная версия всегда возвращает пустой список .

1 Ответ

12 голосов
/ 08 июля 2011

Ну, эти два, очевидно, очень похожи, так почему бы не посмотреть подробно, где они не согласны?Рекурсивная часть в обоих случаях одинакова, поэтому сначала мы можем сказать, что обе версии делают одно и то же в непустых списках.Это звучит неправильно, потому что они дают разные результаты, но на самом деле это правда, что они выполняют одну и ту же операцию с результатом рекурсивного вызова .

Базовый случай из правильной версии: permute [] = [[]], что говорит само за себя.Базовый случай из первой версии, однако, подразумевается в понимании списка.Учитывая определение:

permute xs = [y:ps | (y,ys) <- selections xs, ps <- permute ys]

... мы можем заменить xs в [], чтобы посмотреть, что произойдет:

permute [] = [y:ps | (y,ys) <- selections [], ps <- permute ys]

Учитывая определение selections [] = [], мы можемупростим до:

permute [] = [y:ps | (y,ys) <- [], ps <- permute ys]

... из которого видно, что никаких результатов не генерируется, поэтому понимание всего списка пустое, упрощаясь до:

permute [] = []

Теперь,рассмотрим последний рекурсивный шаг перед базой, подставив [x] в качестве аргумента:

permute [x] = [y:ps | (y,ys) <- selections [x], ps <- permute ys]

Определение selections равно selections (x:xs) = (x, xs) : [ (y, x:ys) | (y,ys) <- selections xs ], подстановка в [x] дает selections [x] = (x, []) : [ (y, x:ys) | (y,ys) <- selections [] ].selections [] оценивается как [], поэтому понимание всего списка также уменьшается до [], давая selections [x] = (x, []) : [] или просто selections [x] = [(x, [])].

Подставьте это в permute, как указано выше:

permute [x] = [y:ps | (y,ys) <- [(x, [])], ps <- permute ys]

В списке только один элемент, поэтому мы можем игнорировать привязку понимания <- и подставить напрямую:

permute [x] = [y:ps | (y,ys) = (x, []), ps <- permute ys]

permute [x] = [ x:ps | ps <- permute []]

Установив, что permute [] оценивается как [], мы также можем подставить это значение и обнаружить, что понимание списка снова уменьшается до []:

permute [x] = []

... чтолегко обобщается на возвращение [] для любого ввода.В рабочей версии, однако, используется следующее определение:

permute [] = [[]]

При окончательном сокращении последнего рекурсивного шага это заменяет следующие:

permute [x] = [ x:ps | ps <- permute []]

permute [x] = [ x:ps | ps <- [[]] ]

Поскольку ps привязан к чему-то с одним элементом, мы снова можем заменить напрямую:

permute [x] = (x:[])

То есть, просто говоря, что permute [x] = [x].

...