Eri*_*ikR 5 monads haskell list-comprehension
这是一种解决欧拉问题43的方法(如果没有给出正确答案,请告诉我).是否有monad或其他合成糖可以帮助跟踪notElem
条件?
toNum xs = foldl (\s d -> s*10+d) 0 xs
numTest xs m = (toNum xs) `mod` m == 0
pandigitals = [ [d0,d1,d2,d3,d4,d5,d6,d7,d8,d9] |
d7 <- [0..9],
d8 <- [0..9], d8 `notElem` [d7],
d9 <- [0..9], d9 `notElem` [d8,d7],
numTest [d7,d8,d9] 17,
d5 <- [0,5], d5 `notElem` [d9,d8,d7],
d3 <- [0,2,4,6,8], d3 `notElem` [d5,d9,d8,d7],
d6 <- [0..9], d6 `notElem` [d3,d5,d9,d8,d7],
numTest [d6,d7,d8] 13,
numTest [d5,d6,d7] 11,
d4 <- [0..9], d4 `notElem` [d6,d3,d5,d9,d8,d7],
numTest [d4,d5,d6] 7,
d2 <- [0..9], d2 `notElem` [d4,d6,d3,d5,d9,d8,d7],
numTest [d2,d3,d4] 3,
d1 <- [0..9], d1 `notElem` [d2,d4,d6,d3,d5,d9,d8,d7],
d0 <- [1..9], d0 `notElem` [d1,d2,d4,d6,d3,d5,d9,d8,d7]
]
main = do
let nums = map toNum pandigitals
print $ nums
putStrLn ""
print $ sum nums
Run Code Online (Sandbox Code Playgroud)
例如,在这种情况下,赋值d3
不是最佳的 - 它确实应该在numTest [d2,d3,d4] 3
测试之前移动.但是,这样做意味着要更改一些要从要检查的列表中notElem
删除的测试d3
.由于连续notElem
列表是通过将最后选择的值输入到前一个列表而获得的,所以看起来这应该是可行的 - 不知何故.
更新:以下是与UniqueSel
Louis'monad 重写的上述程序:
toNum xs = foldl (\s d -> s*10+d) 0 xs
numTest xs m = (toNum xs) `mod` m == 0
pandigitalUS =
do d7 <- choose
d8 <- choose
d9 <- choose
guard $ numTest [d7,d8,d9] 17
d6 <- choose
guard $ numTest [d6,d7,d8] 13
d5 <- choose
guard $ d5 == 0 || d5 == 5
guard $ numTest [d5,d6,d7] 11
d4 <- choose
guard $ numTest [d4,d5,d6] 7
d3 <- choose
d2 <- choose
guard $ numTest [d2,d3,d4] 3
d1 <- choose
guard $ numTest [d1,d2,d3] 2
d0 <- choose
guard $ d0 /= 0
return [d0,d1,d2,d3,d4,d5,d6,d7,d8,d9]
pandigitals = map snd $ runUS pandigitalUS [0..9]
main = do print $ pandigitals
Run Code Online (Sandbox Code Playgroud)
Lou*_*man 10
当然.
newtype UniqueSel a = UniqueSel {runUS :: [Int] -> [([Int], a)]}
instance Monad UniqueSel where
return a = UniqueSel (\ choices -> [(choices, a)])
m >>= k = UniqueSel (\ choices ->
concatMap (\ (choices', a) -> runUS (k a) choices')
(runUS m choices))
instance MonadPlus UniqueSel where
mzero = UniqueSel $ \ _ -> []
UniqueSel m `mplus` UniqueSel k = UniqueSel $ \ choices ->
m choices ++ k choices
-- choose something that hasn't been chosen before
choose :: UniqueSel Int
choose = UniqueSel $ \ choices ->
[(pre ++ suc, x) | (pre, x:suc) <- zip (inits choices) (tails choices)]
Run Code Online (Sandbox Code Playgroud)
然后你像处理List monad一样对待它,guard
强制选择,除了它不会多次选择一个项目.一旦你有了UniqueSel [Int]
计算,map snd (runUS computation [0..9])
就把它[0..9]
作为选择的选择.