어색한 사 transformer 택
-
10-07-2019 - |
문제
문제 해결에서 Google 코드 잼(2009.1A.:"멀티 기본 행복")나가 어려운(코드의 현명한)솔루션에 관심이 있어요 그것은 어떻게 향상 시킬 수 있습니다.
문제를 설명하시는 것은:최소보다 더 큰 1 는 반복적으로 합산하는 사각형의 자리에 도달 1,에 대한 기준에서 주어진 목록입니다.
또 설명에서 의사켈(코드는 그것을 해결하는 경우 elem
수상 작업에 대한 무한한 목록):
solution =
head . (`filter` [2..]) .
all ((1 `elem`) . (`iterate` i) . sumSquareOfDigitsInBase)
내 어색한 솔루션:
- 여 어색한 평가가 이런 종류의 코드:
happy <- lift . lift . lift $ isHappy Set.empty base cur
- 나는,그러나 다른 클라이언트의 결과를 isHappy 기능입니다.를 사용하여 주일에 대한 memoized 결과 지도입니다.
- 을 찾으려고 첫 번째 솔루션을 사용 하지 않았
head
고filter
(다음과 같은 의사일 위에는 않습니다)때문에,계산지 않은 순수(상태가 변경).그래서 내가 반복 사용하여 StateT 카운터 및 MaybeT 을 종료한 계산을 때에 조건을 보유하고 있습니다. - 이미 내에서
MaybeT (StateT a (State b))
, 는 경우,이 조건을 붙들지 않는 하나의 기초가 필요 없을 확인하는 다른 것들,그래서 다른MaybeT
에서 스택입니다.
코드:
import Control.Monad.Maybe
import Control.Monad.State
import Data.Maybe
import qualified Data.Map as Map
import qualified Data.Set as Set
type IsHappyMemo = State (Map.Map (Integer, Integer) Bool)
isHappy :: Set.Set Integer -> Integer -> Integer -> IsHappyMemo Bool
isHappy _ _ 1 = return True
isHappy path base num = do
memo <- get
case Map.lookup (base, num) memo of
Just r -> return r
Nothing -> do
r <- calc
when (num < 1000) . modify $ Map.insert (base, num) r
return r
where
calc
| num `Set.member` path = return False
| otherwise = isHappy (Set.insert num path) base nxt
nxt =
sum . map ((^ (2::Int)) . (`mod` base)) .
takeWhile (not . (== 0)) . iterate (`div` base) $ num
solve1 :: [Integer] -> IsHappyMemo Integer
solve1 bases =
fmap snd .
(`runStateT` 2) .
runMaybeT .
forever $ do
(`when` mzero) . isJust =<<
runMaybeT (mapM_ f bases)
lift $ modify (+ 1)
where
f base = do
cur <- lift . lift $ get
happy <- lift . lift . lift $ isHappy Set.empty base cur
unless happy mzero
solve :: [String] -> String
solve =
concat .
(`evalState` Map.empty) .
mapM f .
zip [1 :: Integer ..]
where
f (idx, prob) = do
s <- solve1 . map read . words $ prob
return $ "Case #" ++ show idx ++ ": " ++ show s ++ "\n"
main :: IO ()
main =
getContents >>=
putStr . solve . tail . lines
다른 참가자를 사용하여 메가 더 좋은 해결책, 하지만 문제를 해결했다.나의 질문에 대한 반복적인 개선을 나의 코드입니다.
해결책
귀하의 솔루션은 Monads의 사용 (및 남용)이 확실히 어색합니다.
- 여러 변압기를 쌓아서 모나드를 구축하는 것이 일반적입니다.
- 덜 평소이지만 때로는 여전히 발생합니다.
- 여러 변압기를 쌓는 것은 매우 드문 일입니다
- Maybet을 사용하여 루프를 방해하는 것이 훨씬 더 드문 일입니다.
코드가 너무 무의미합니다.
(`when` mzero) . isJust =<<
runMaybeT (mapM_ f bases)
읽기 쉬운 대신
let isHappy = isJust $ runMaybeT (mapM_ f bases)
when isHappy mzero
이제 기능 solve1에 초점을 맞추려면 단순화하겠습니다. 그렇게 쉬운 방법은 내부 Maybet Monad를 제거하는 것입니다. 행복한 숫자가 발견 될 때 깨지는 영원한 루프 대신, 다른 길을 가고 숫자가 행복하지 않은 경우에만 되 살릴 수 있습니다.
또한, 당신은 실제로 주 모나드가 필요하지 않습니까? 하나는 항상 상태를 명백한 주장으로 대체 할 수 있습니다.
이러한 아이디어를 적용하는 것은 이제 훨씬 좋아 보인다 :
solve1 :: [Integer] -> IsHappyMemo Integer
solve1 bases = go 2 where
go i = do happyBases <- mapM (\b -> isHappy Set.empty b i) bases
if and happyBases
then return i
else go (i+1)
나는 그 코드에 더 행복 할 것입니다. 나머지 솔루션은 괜찮습니다. 나를 귀찮게하는 한 가지는 모든 하위 문제에 대한 메모 캐시를 버린다는 것입니다. 그 이유가 있습니까?
solve :: [String] -> String
solve =
concat .
(`evalState` Map.empty) .
mapM f .
zip [1 :: Integer ..]
where
f (idx, prob) = do
s <- solve1 . map read . words $ prob
return $ "Case #" ++ show idx ++ ": " ++ show s ++ "\n"
대신 재사용 된 경우 솔루션이 더 효율적이지 않습니까?
solve :: [String] -> String
solve cases = (`evalState` Map.empty) $ do
solutions <- mapM f (zip [1 :: Integer ..] cases)
return (unlines solutions)
where
f (idx, prob) = do
s <- solve1 . map read . words $ prob
return $ "Case #" ++ show idx ++ ": " ++ show s
다른 팁
Monad* 클래스는 반복적 인 리프팅의 필요성을 제거하기 위해 존재합니다. 다음과 같은 서명을 변경하는 경우 :
type IsHappyMemo = Map.Map (Integer, Integer) Bool
isHappy :: MonadState IsHappyMemo m => Set.Set Integer -> Integer -> Integer -> m Bool
이렇게하면 대부분의 리프트를 제거 할 수 있습니다. 그러나 가장 긴 리프트 시퀀스는 스테이트 내부의 상태 모나드이기 때문에 제거 할 수 없으므로 Monadstate 유형 클래스를 사용하면 내부 상태에 도달 해야하는 외부 스타트를 제공합니다. 당신은 당신의 주 모나드를 새로운 유형으로 감싸고 기존 모나드 수업과 비슷한 모나 디 행복 수업을 만들 수 있습니다.
ListT
(서 목록 패키지)않은 훨씬 더 좋은보다 작업 MaybeT
를 중지에 계산할 때 필요합니다.
solve1 :: [Integer] -> IsHappyMemo Integer
solve1 bases = do
Cons result _ <- runList . filterL cond $ fromList [2..]
return result
where
cond num = andL . mapL (isHappy Set.empty num) $ fromList bases
몇 가지 정교하는 방법에 이동:
을 사용했는 정규 목록 코드는 것이 이렇게 되어 있었다:
solve1 bases = do
result:_ <- filterM cond [2..]
return result
where
cond num = fmap and . mapM (isHappy Set.empty num) bases
이 계산에서 발생 State
되었지만,경우에는 우리가 결과를 얻을 상태,우리는 문제가 있기 때문에, filterM
실행 monadic 조건자를 위한 모든 요소의 [2..]
, 는 무한 목록입니다.
로 monadic 목록 filterL cond (fromList [2..])
목록을 나타내는 우리에 액세스할 수 있 한 번에 하나의 항목으로 monadic 작업,그래서 우리의 monadic 조건자 cond
지 않습니다 실제로 실행(과에 영향을 미치는 상태)지 않으면 우리가 소비하는 목록은 해당 항목입니다.
마찬가지로,구현 cond
용 andL
우리가하지 않을 계산 및 업데이트 상태는 경우에 우리는 이미 받았 False
결과 중 하나에서 isHappy Set.empty num
을 계산합니다.