먼저 피보나치 알고리즘이 사용됨을 확인하십시오. 아이디어는 튜플 (0, 1)
에서 시작하여 다음을 (1, 0 + 1)
, 다음을 (1, 1 + 1)
, (2, 2 + 1)
, (3, 3 + 2)
등으로 찾습니다. 일반적으로이 단계는 \(a, b) -> (b, a + b)
입니다. 이 튜플에는 피보나치 수를 볼 수 있습니다.
첫 즉 내부 DO의 성명, 어떤 (A, B) <에 결과를 -get 않습니다 내부에 무슨 일
?
하스켈은 문장만을 가지고 있습니다.
y <- x
은 완전한 표현식이 아닙니다. x >>= \y ->
과 유사합니다.
y <- x
m
완전한 표현식은 x >>= \y -> m
과 동일합니다. y <- n
형식이 아닌 n
줄은 _ <- n
(let
줄을 제외하고 다른 일부는 내가 잊지 않음)와 동일합니다.
이것을 사용하여 우리는 표기 할 수 있습니다.
fib n =
flip evalState (0, 1)
(forM
[0..(n-1)]
(\_ -> get >>= (\(a, b) -> put (b, a + b)))
>>= (\_ -> get >>= (\(a, b) -> return a)))
)
지금 그것은 단지 등 이해 >>=
, return
, get
, put
및에 관한 것입니다.
상태는 실제로는 s -> (s, a)
유형의 기능입니다. 그들은 초기 상태를 취하고 다음 상태에 다른 값을 더합니다.
m >>= n
a.k.a "bind"는 Monad m => m a -> (a -> m b) -> m b
유형입니다.
m >>= n ::
( s -> (s, a))
-> (a -> s -> (s, b))
-> ( s -> (s, b))
m
에 의해 반환 a
가 가지고있는이 n
에 전달 될 : 우리의 모나드가 State s
인 경우,이 동일합니다. 다른 무엇을 추측 할 수 있습니까? 우리는 주정부도 통과 할 것으로 예상하므로 m
으로 반환되는 주정부는 n
으로 전달되어야합니다. m >>= n
함수는 n
이 반환하는 상태와 값을 반환해야합니다.
return = flip (,)
get :: State s s
이 get :: s -> (s, s)
에 해당합니다 :
get = join (,)
put :: s -> State s()
또는 put :: s -> s -> (s,())
:
다음
return :: a -> s -> (s, a)
에 해당
m >>= n = uncurry (flip n) . m
return :: Monad m => a -> m a
: 우리는 다음 바인딩을 구현하는 방법을 알고
put s _ = (s,())
evalState :: s -> State s a -> a
또는 evalState :: s -> (s -> (s, a)) -> a
:
evalState s f = snd (f s)
당신은 모든 정의를 확장하여 예에서 일어나는 정확하게 볼 수 있습니다. 그러나 직감만으로도 충분합니다.
forM
[0..(n-1)]
(\_ -> get >>= (\(a, b) -> put (b, a + b)))
우리는 숫자 때문에 첫 번째 인수가 삭제됩니다 n - 1
-0
를하는 것에 대한 걱정하지 않는다. get
은 현재 상태를 검색하고 put
은 새 상태를 씁니다. 우리는 이것을 n
번합니다.
>>= (\_ -> get >>= (\(a, b) -> return a)))
누적 값 (단위)이 중요하지 않으므로 첫 번째 매개 변수가 삭제됩니다. 그런 다음 현재 상태를 가져 와서 쌍의 첫 번째 요소 만 투사합니다. 이것이 우리가 찾고있는 최종 답입니다.
flip evalState (0, 1) …
마지막으로 (0, 1)
부터 시작하여 실행합니다.
이 구현에는 몇 가지 정리가 있습니다. 첫째, 우리는 범위 [0..(n-1)]
에 대해 신경 쓰지 않습니다. 우리는 단지 n
번 작업을 반복하는 것에 관심이 있습니다.이 작업을 수행하기위한보다 직접적인 방법은 다음
replicateM n (get >>= \(a, b) -> put (b, a + b))
결과입니다 미사용 단위의 목록, 그래서 더 효율적인 버전이 : 이미있는 기능
replicateM_ n (get >>= \(a, b) -> put (b, a + b))
있다
은
get
에 이어
put
이라는 공통 패턴이 있으며
modify
이라는 이름으로
\f -> get >>= put . f
으로 정의됩니다. 따라서 :
replicateM_ n (modify (\(a, b) -> (b, a + b)))
는 그런 부분이 :
>>= (\_ -> get >>= (\(a, b) -> return a)))
우리가 >>
을 사용할 수 있습니다 이전 결과에 대해 걱정하지 않는 모든 시간.
>> get >>= (\(a, b) -> return a))
입니다 :
>> get >>= return . fst
m >>= return . f
단순화 fmap f m
에 : 이제
>> fmap fst get
우리가, 총 :
fib n =
evalState
( replicateM_ n (modify (\(a, b) -> (b, a + b)))
>> fmap fst get
)
(0, 1)
우리는 또한 비교를 위해 사용할 수 :
fib =
fst
. flip evalState (0, 1)
. (>> get)
. flip replicateM_ (modify (snd &&& uncurry (+)))
가 왜 여기 상태 모나드를 사용하고자하는 것 :
fib n =
fst
(evalState
( replicateM_ n (modify (\(a, b) -> (b, a + b)))
>> get
)
(0, 1)
)
그리고 내가 바보이기 때문에
?
그렇지 않을 수도 있습니다. 이것은 상태 값만 사용하기 때문에 분명합니다. 다른 값은 항상 단위이며 버려집니다. 즉, 처음에는 n
(즉 피보나치 수를 찾을 필요가 있습니다) 만 있으면 누적 튜플 만 있으면됩니다.
때로는 h . g . f
과 같은 구성 문자열이 있다고 생각하지만 한 개 대신 두 개의 인수를 보내려는 경우가 있습니다. 즉, State
이 적용될 수 있습니다.
일부 함수가 읽히고 일부 (두 번째 인수)의 상태를 쓰거나 두 가지 모두를 수행하면 State
이 계산서에 적합합니다. 독자 만있는 경우 Reader
을 사용하고 작성자 만있는 경우 Writer
을 사용하십시오.
State Monad를보다 잘 활용하기 위해이 예를 변경할 수 있습니다. 튜플을 사라지게 만들거야!
fib =
flip evalState 0
. foldr (=<<) (return 1)
. flip replicate (\x -> get >>= \y -> put x $> x + y)
2)가 간다면 실제 코드에서는 장난감의 예가 아닙니다. –