Haskell: положить в государственную монаду, кажется, исключено
Я пишу программу для раздачи пиццы людям; каждый человек получит одну пиццу, в идеале своего любимого типа, если на складе не закончится, и в этом случае ему дадут рекурсивный следующий тип.
Мой подход состоит в том, чтобы вычислить ((User, Pizza), Int)
на сумму, которую человек хотел бы сказать пиццу, отсортировать ее и обработать с помощью государственной монады, чтобы вести учет инвентаря.
Программа написана и проверяет тип:
allocatePizzasImpl :: [((User, Pizza), Int)]
-> State [(Pizza, Int)] [(User, Pizza)]
allocatePizzasImpl [] = return []
allocatePizzasImpl ((user, (flavor, _)):ranks) =
do inventory <- get
-- this line is never hit
put $ updateWith inventory (\i -> if i <= 0
then Nothing
else Just $ i - 1) flavor
next <- allocatePizzasImpl $ filter ((/= user) . fst) ranks
return $ (user, flavor) : next
и у меня есть вспомогательная функция для извлечения результата:
allocatePizzas :: [Pizza]
-> [((User, Pizza), Int)]
-> [(User, Pizza)]
allocatePizzas pizzas rank = fst
. runState (allocatePizzasImpl rank)
$ buildQuotas pizzas
но линия обозначена -- this line is never hit
... никогда не попадает в точки останова GHCI; Более того, если я прерву ответный звонок, GHCI говорит inventory
не в сфере.
При запуске результатом является присвоение одной и той же пиццы (с одним инвентарным счетом) всем пользователям. Что-то идет не так, но я абсолютно не знаю, как поступить. Я новичок в Haskell, поэтому любые комментарии по стилю также приветствуются =)
Спасибо!
PS: для полноты updateWith
определяется как:
updateWith :: (Eq a, Eq b)
=> [(a, b)] -- inventory
-> (b -> Maybe b) -- update function; Nothing removes it
-> a -- key to update
-> [(a, b)]
updateWith set update key =
case lookup key set of
Just b -> replace set
(unwrapPair (key, update b))
(fromMaybe 0 $ elemIndex (key, b) set)
Nothing -> set
where replace :: [a] -> Maybe a -> Int -> [a]
replace [] _ _ = []
replace (_:xs) (Just val) 0 = val:xs
replace (_:xs) Nothing 0 = xs
replace (x:xs) val i = x : (replace xs val $ i - 1)
unwrapPair :: Monad m => (a, m b) -> m (a, b)
unwrapPair (a, mb) = do b <- mb
return (a, b)
2 ответа
Я думаю твоя функция replace
сломано:
replace (_:xs) (Just val) 0 = val:xs
Это не обращает никакого внимания на значение, которое он заменяет. Не было ли у вас намерения заменить только пару, соответствующую key
?
Я думаю ты хочешь
updateWith [] e k = []
updateWith ((k', v):kvs) e k
| k' == k = case e v of
Just v' -> (k, v'):kvs
Nothing -> kvs
| otherwise = (k', v) : updateWith kvs e k
Проблема (игнорирование других концептуальных вещей, упомянутых комментаторами) оказалась в использовании fst
получение результата от государства по некоторым причинам не приведет к тому, что государство будет фактически вычислено. Выполнение результата через seq
починил это.
Мне было бы интересно узнать, почему это так!
Редактировать: Как отметил Даниэль Вагнер в комментариях, я фактически не использовал инвентарь, который оказался настоящей ошибкой. Отметить это как принятый.