Различное поведение монады состояния Хаскеля при вызовах четных и нечетных функций - PullRequest
0 голосов
/ 01 декабря 2018

Я хочу сделать функцию, которая добавляет 5 для нечетных вызовов и вычитает 6 для четных вызовов с использованием монад состояния.

f 5 =  10
f 7 = 1
f 4 = 9
f 2 = -4

0 - это четное число, поэтому f 5 добавляет 5. 1 нечетно, так что f 7 вычитает6 и т. Д.

Что у меня сейчас есть:

data Parity = Even | Odd deriving (Show, Eq)

not' :: Parity -> Parity
not' Even = Odd
not' Odd = Even

isOdd :: Int -> State Parity Int
isOdd x = state $ \(p) -> (if p == Odd then x + 5 else x - 6, not' p)

g n = do isOdd n

apply n = runState (g n) Even

Я пытался написать это так, но каждый раз, когда применяется «применить», состояние не сохраняется.Это только добавить 5 из-за четности в конце.Как заставить его спасти государство и только инициализировать его один раз, а не каждый раз?

1 Ответ

0 голосов
/ 01 декабря 2018

Этот ответ Я написал несколько дней назад, может быть полезным.Короче говоря, State s - это просто удобный способ имитации "функций с состоянием" f :: a -> b как чистых функций f :: (a,s) -> (b,s).Чтобы соответствовать структуре монады, хотя они карри, так (примерно) в форме f :: a -> s -> (b,s).

Тип State s b примерно равен s -> (b,s), что может быть прочитано как «вычисление, которое возвращает значение b и конечное состояние s и которое требует, чтобы начальное состояние s былобежать".Поэтому монадическая функция a -> State s b - это функция, которая принимает входные данные a и может запускаться при заданном начальном состоянии s для получения значения b и конечного состояния s.

Ваша функция isOdd,

isOdd x :: Int -> State Parity Int
isOdd x = state $ \p -> (if p == Odd then x + 5 else x - 6, not' p)

, что примерно,

isOdd' x :: Int -> Parity -> (Int,Parity)
isOdd' x p = (if p == Odd then x + 5 else x - 6, not' p)

А ваш звонок,

 apply n = runState (isOdd n) Even 

, примерно,

 apply' n = isOdd' x Even

Вот и все.По сути, вы вычисляете

 apply' n = --definition of apply'
            isOdd' n Even
            -- definition of isOdd'
            (\x p -> (if p == Odd then x + 5 else x - 6, not' p)) n Even
            -- application to the arguments `n` and `Even` 
            = (if Even == Odd then n + 5 else n - 6, not' Even)
            -- simplifying
            = (n - 6, Odd)

,

apply' n = (n - 6, Odd)

Вот пример того, как правильно упорядочить вашу функцию,

f :: Int -> State Parity Int
f n = isOdd n >>= (\x -> isOdd x) 

или эквивалентно

f :: Int -> State Parity Int
f n = do x <- isOdd n
         isOdd x

Когда вы запускаете его, например, через apply n = runState (f n) Even, вы сначала запускаете isOdd n Even, чтобы получить результат m и новое конечное состояние, которое будет False, а затем - isOdd m False.

...