2015-04-15 6 views
2

Я пишу программу для размещения пиццы для людей; каждый человек получит одну пиццу, в идеале своего любимого типа, если запасы не закончились, и в этом случае они будут рекурсивно возвращаться к их следующему любимому типу.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) 
+0

Можете ли вы включить определение 'buildQuotas'? Или, возможно, вы можете уменьшить свой код до [MCVE] (http://stackoverflow.com/help/mcve). – bheklilr

+2

Кроме того, 'allocatePizzasImpl' не выполняет typecheck (пытается сопоставить' (flavor, _) 'pattern с типом' Int'). –

ответ

1

Я думаю, что ваша функция 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 
+0

Это намного лучше, чем моя. Благодаря! –

0

выпуска (игнорируя другие концептуальные вещи, упомянутых комментаторами) оказалось использовать fst для извлечения результата от государства будет по каким-то причинам не вызывают государства на самом деле вычисляться. Запуск результата через seq зафиксировал его.

Мне было бы интересно узнать , почему это случай, хотя!

Редактировать: Как отметил Даниэль Вагнер в комментариях, я фактически не использовал инвентарь, который оказался настоящей ошибкой. Пометить это как принято.

+1

Просто стандартная лень. Зачем вычислять полученное состояние, если никто не заботится о том, что это такое? (Обратите внимание, что вы фактически не используете 'inventory', чтобы принимать какие-либо решения, кроме того, что должно быть следующим инвентарем!) –

+0

Фантастическая точка. Не использование инвентаря - настоящая ошибка. Ура! –