Недетерминированная сортировка слиянием не упорядочивает перестановки лексикографически

Я пытался воспроизвести отступление, упомянутое в Все виды перестановок (Functional Pearl ) Кристиансена, Даниленко и Дилуса, статья для предстоящей конференции ICFP 2016. В разделе 8 (Заключительные замечания) утверждается, что, выбирая конкретный недетерминированный предикат, монадическая сортировка слиянием может производить все перестановки последовательности в лексикографическом виде. заказ.

Мы рассмотрели только недетерминированный предикат coinCmp, хотя есть и другие недетерминированные предикаты, которые можно использовать для изменения порядка перечисления. Например, следующая функция поднимает предикат cmp до недетерминированного контекста.

liftCmp :: MonadPlus μ
        ⇒ (α → α → Bool) → Cmp α μ
liftCmp p x y = return (p x y) ⊕ return (not (p x y))

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

Я почти уверен, что то, что я написал здесь, - это сортировка слиянием, но при запуске порядок не такой, как рекламируется.

import Control.Applicative (Alternative((<|>)))
import Control.Monad (MonadPlus, join)
import Data.Functor.Identity (Identity)

-- Comparison in a context
type Comparison a m = a -> a -> m Bool

-- Ordering lifted into the Boring Monad
boringCmp :: (a -> a -> Bool) -> Comparison a Identity
boringCmp p x y = return (p x y)

-- Arbitrary ordering in a non-deterministic context
cmp :: MonadPlus m => Comparison a m
cmp _ _ = return True <|> return False

-- Ordering lifted into a non-deterministic context
liftCmp :: MonadPlus m => (a -> a -> Bool) -> Comparison a m
liftCmp p x y = let b = p x y in return b <|> return (not b)

mergeM :: Monad m => Comparison a m -> [a] -> [a] -> m [a]
mergeM _ ls         []         = return ls
mergeM _ []         rs         = return rs
mergeM p lls@(l:ls) rrs@(r:rs) = do
    b <- p l r
    if b
    then (l:) <$> mergeM p ls rrs
    else (r:) <$> mergeM p lls rs

mergeSortM :: Monad m => Comparison a m -> [a] -> m [a]
mergeSortM _ []  = return []
mergeSortM _ [x] = return [x]
mergeSortM p xs  = do
    let (ls, rs) = deinterleave xs
    join $ mergeM p <$> mergeSortM p ls <*> mergeSortM p rs
  where
    deinterleave :: [a] -> ([a], [a])
    deinterleave [] = ([], [])
    deinterleave [l] = ([l], [])
    deinterleave (l:r:xs) = case deinterleave xs of (ls, rs) -> (l:ls, r:rs)
λ mergeSortM (boringCmp (<=)) [2,1,3] :: Identity [Int]
Identity [1,2,3]

λ mergeSortM cmp [2,1,3] :: [[Int]]
[[2,3,1],[2,1,3],[1,2,3],[3,2,1],[3,1,2],[1,3,2]]

λ mergeSortM (liftCmp (<=)) [2,1,3] :: [[Int]]
[[1,2,3],[2,1,3],[2,3,1],[1,3,2],[3,1,2],[3,2,1]]

И фактический лексикографический порядок для справки

λ sort it
[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

person R B    schedule 07.08.2016    source источник
comment
Что произойдет, если вы передадите [1,2,3] в качестве аргумента?   -  person Benjamin Hodgson♦    schedule 07.08.2016
comment
Случайно работает, попробуй [1..4].   -  person R B    schedule 07.08.2016


Ответы (1)


Давайте попробуем вариант deinterleave, который разделяет первую и последнюю половину списка вместо разделения элементов с четным и нечетным индексом, как в опубликованном коде:

deinterleave :: [a] -> ([a], [a])
deinterleave ys = splitAt (length ys `div` 2) ys

Результат:

> mergeSortM (liftCmp (<=)) [2,1,3] :: [[Int]]
[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

К сожалению, это не решает проблему, как я сначала надеялся, как указывает Роуэн Блаш ниже. :-/

person chi    schedule 07.08.2016
comment
Это просто шанс, см. mergeSortM (liftCmp (<=)) [1,2,3] :: [[Int]][[1,2,3],[2,1,3],[2,3,1],[1,3,2],[3,1,2],[3,2,1]]. - person R B; 07.08.2016
comment
@RowanBlush Интересно. Тем не менее, определение deinterleave влияет на конечный результат. Интересно, какой из них использовали авторы — и действительно, есть ли разумное определение, которое действительно приводит к предполагаемому поведению. - person chi; 07.08.2016