#haskell
Вопрос:
Предположим, у меня есть какой-то очень простой код:
import qualified Data.Map as Map import Data.Maybe(fromJust) keySum :: Map.Map Char Int -gt; Char -gt; Char -gt; Either String Int keySum m key1 key2 | val1 == Nothing = Left $ show val1 " not in map" | val2 == Nothing = Left $ show val2 " not in map" | otherwise = Right $ val1' val2' where val1 = Map.lookup key1 m val2 = Map.lookup key2 m val1' = fromJust val1 val2' = fromJust val2
Конечно, я не хочу использовать fromJust
, и предпочел бы, чтобы шаблон совпадал с результатом Map.lookup
здесь.
Но как я могу этого достичь, не оценивая Map.lookup
дважды или не передавая его через какую-либо функцию-оболочку?
Комментарии:
1. Я не понимаю, почему это неправильно использовать
fromJust
. Вы можете избежать дополнительных переменных и записатьotherwise = Right $ fromJust val1 fromJust val2
2. Или, если вы хотите быть особенно крутым, сделайте что-нибудь подобное:
| otherwise = Right . fromJust $ ( ) lt;$gt; val1 lt;*gt; val2
3. @user1984 Вы всегда должны предполагать, что его неправильно использовать
fromJust
, если только вы не видите очень веской причины, почему это правильно (единственное, что я могу придумать навскидкуmfix
Maybe
).4. О, спасибо, что дали мне знать @JosephSible-RestorateMonica, я очень новичок в Haskell и только начинаю учиться.
Ответ №1:
Вы можете использовать защиту от шаблонов:
keySum m key1 key2 | Just val1 lt;- Map.lookup key1 m , Just val2 lt;- Map.lookup key2 m = Right $ val1 val2 | key1 `Map.notMember` m = Left $ show key1 " not in map" | otherwise = Left $ show key2 " not in map"
Но, возможно, старый добрый case
на самом деле более чистый вариант:
keySum m k₀ k₁ = case (`Map.lookup` m) lt;$gt; [k₀,k₁] of [Just v₀, Just v₁] -gt; Right $ v₀ v₁ [Nothing, _ ] -gt; Left $ show k₀ " not in map" _ -gt; Left $ show k₁ " not in map"
Это можно еще больше упростить, заметив, что это всего лишь стандартная прикладная цепочка:
keySum m k₀ k₁ = ( ) lt;$gt; lku k₀ lt;*gt; lku k₁ where lku k = case Map.lookup k m of Just v -gt; Right v Nothing -gt; Left $ show k " not in map"
Если вы разбираетесь в шаблонах, это также можно написать таким образом (лично я не такой поклонник этого расширения):
{-# LANGUAGE ViewPatterns #-} keySum m k₀ k₁ = ( ) lt;$gt; lku k₀ lt;*gt; lku k₁ where lku ((`Map.lookup` m) -gt; Just v) = Right v lku k = Left $ show k " not in map"
И, наконец, нет причин ограничивать это только двумя ключами, с таким же успехом это может быть последовательность по всему списку произвольной длины. Кстати, нет никаких причин выбирать конкретные типы.
import Data.Traversable (forM) keySum :: (Ord a, Show a, Num b) =gt; Map.Map a b -gt; [a] -gt; Either String b keySum m ks = sum lt;$gt; forM ks `id` k -gt; case Map.lookup k m of Just v -gt; Right v Nothing -gt; Left $ show k " not in map"
Комментарии:
1. Ах, используя прикладной оператор. Спасибо.