Использование списков в модель недетерминированности проблематично, если входы могут принимать бесконечно много значений. Например
pairs = [ (a,b) | a <- [0..], b <- [0..] ]
Это вернет [(0,1),(0,2),(0,3),...]
и никогда не сможет показать вам любую пару, чей первый элемент не является 0
.
Используя функцию Cantor pairing, чтобы свернуть список списков в один список, можно обойти эту проблему. Например, мы можем определить оператор типа привязки, который упорядочивает свои выходы более разумно с помощью
(>>>=) :: [a] -> (a -> [b]) -> [b]
as >>>= f = cantor (map f as)
cantor :: [[a]] -> [a]
cantor xs = go 1 xs
where
go _ [] = []
go n xs = hs ++ go (n+1) ts
where
ys = filter (not.null) xs
hs = take n $ map head ys
ts = mapN n tail ys
mapN :: Int -> (a -> a) -> [a] -> [a]
mapN _ _ [] = []
mapN n f [email protected](h:t)
| n <= 0 = xs
| otherwise = f h : mapN (n-1) f t
Если мы теперь завершим это как монаду, мы можем перечислить все возможные пары
newtype Select a = Select { runSelect :: [a] }
instance Monad Select where
return a = Select [a]
Select as >>= f = Select $ as >>>= (runSelect . f)
pairs = runSelect $ do
a <- Select [0..]
b <- Select [0..]
return (a,b)
В результате получается
>> take 15 pairs
[(0,0),(0,1),(1,0),(0,2),(1,1),(2,0),(0,3),(1,2),(2,1),(3,0),(0,4),(1,3),(2,2),(3,1),(4,0)]
что является гораздо более желательным результатом. Однако, если бы мы попросили три раза, заказ на выходах не был таким "приятным", и мне даже не ясно, что все выходы в конечном итоге включены -
>> take 15 triples
[(0,0,0),(0,0,1),(1,0,0),(0,1,0),(1,0,1),(2,0,0),(0,0,2),(1,1,0),(2,0,1),(3,0,0),(0,1,1),(1,0,2),(2,1,0),(3,0,1),(4,0,0)]
Обратите внимание, что (2,0,1)
появляется перед (0,1,1)
в упорядочении - моя интуиция говорит, что хорошее решение этой проблемы будет упорядочивать выходы в соответствии с некоторым понятием "размер" , что может быть явным входом в алгоритм, или может быть дано неявно (как в этом примере, где "размер" ввода - это его позиция во входных списках). При объединении входов "размер" комбинации должен быть некоторой функцией (вероятно, суммой) размера входов.
Есть ли элегантное решение этой проблемы, которое мне не хватает?