그것을 이해하는 가장 좋은 방법은 그것을하는 것입니다. 아래는 대신에 foldlM
사용 하는 구현 foldl
이 foldr
있습니다. 그것은 좋은 운동이며, 시도하고 나중에 제안 할 솔루션으로옵니다. 이 예제에서는 달성하기 위해 수행 한 모든 추론을 설명합니다. 이는 여러분과 다를 수 있으며 이미 함수 누산기 사용에 대해 알고 있었기 때문에 편견 일 수 있습니다.
1 단계 : 쓰기로하자 시도 foldlM
의 관점에서foldl
-- this doesn't compile because f returning type is (m b) and not just (b)
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f z0 xs
-- So let substitute f by some undefined f'
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' = undefined
-- cool, but f' should use f somehow in order to get the monadic behaviour
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' b a = f somethingIDontkNow
여기서 당신은 그것이 f'
순수 하다는 것을 알고 있으며 f
일치하는 유형 의 결과를 추출해야합니다 . 모나 딕 값을 '추출'하는 유일한 방법 >>=
은 연산자를 사용하는 것입니다. 그러나 이러한 연산자는 사용 후 바로 감싸 야합니다.
결론적으로 : 당신이 끝날 때 마다이 모나드를 완전히 풀고 싶습니다 . 포기하십시오. 올바른 방법이 아닙니다
2 단계 :하자 작성하려고 foldlM
측면에서 foldl
하지만 처음 사용하는 []
접이식으로,이 (즉, 우리가 실제로 사용할 필요가 없습니다 패턴 일치 용이하기 때문에 fold
)
-- This is not very hard. It is pretty standard recursion schema. :)
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
좋아, 쉬웠다. foldl
정의를 목록에 대한 일반적인 정의 와 비교하자
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
myfoldl :: (b -> a -> b) -> b -> [a] -> b
myfoldl f z0 [] = z0
myfoldl f z0 (x:xs) = foldl f (f z0 x) xs
멋있는!! 그들은 거의 동일합니다. 사소한 경우는 거의 똑같습니다. 재귀 사례는 약간 다릅니다. 다음과 같은 내용을 작성하고 싶습니다 foldlM' f (f z0 x) xs
. 그러나 1 단계에서와 같이 컴파일되지 않으므로 OKf
>>=
라고 생각할 수 있습니다. 적용하고 싶지 않습니다. 그런 계산을 잡고로 작성하십시오 . foldlM' f (f z0 x >>=) xs
말이된다면 좀 더 쓰고 싶습니다 ...
3 단계 누적하려는 것은 결과가 아니라 함수 구성이라는 것을 인식하십시오. ( 여기서 게시했기 때문에 이미 알고 있다는 사실에 편견이있을 수 있습니다 ).
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' initFunc xs
where initFunc = undefined :: b -> m b
f' = undefined :: (b -> m b) -> a -> (b -> m b) -- This type signature can be deduce because f' should be applied to initFunc and a's from t a.
initFunc
2 단계 (재귀 적 정의)의 지식 유형 과 사용을 통해이를 추론 할 수 있습니다 initFunc = return
. 의 정의 f'
그것이 알고 완료 할 수 있습니다 f'
사용해야 f
하고 >>=
.
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' return xs z0
-- ^^^^^^
-- |- Initial value
where f' b a = \bvalue -> b bvalue >>= \bresult -> f bresult a -- this is equivalent to (b >=> \result -> f result a) which captures the sequence behaviour of the implementation
-- ^ ^^^^^^ ^^^^^^^
-- | | |- This is the result of previous computation
-- | |- f' should return a function b -> m b. Any time you have to return a function, start writing a lambda
-- |- This b is the accumulated value and has type b -> m b
-- Following the types you can write this with enough practise
보시다시피 그렇게하기가 어렵지 않습니다. 그것은 연습이 필요하지만, 나는 전문적인 하스켈 개발자가 아니며 스스로 할 수 있습니다, 그것은 연습의 문제입니다