Прежде всего, давайте подумаем о нескольких вещах.
- Есть ли у
function1
побочные эффекты? - Есть ли у
function2
побочные эффекты? - Есть ли у
function3
побочные эффекты?
Ответом на все это является совершенно очевидное ДА, потому что они не принимают входных данных, и, вероятно, существуют обстоятельства, которые заставляют вас обходить цикл whileболее одного раза (а не def function3(): return false
).Теперь давайте переделаем эти функции с явным состоянием.
s = initialState
sentinel = true
while(sentinel):
a,b,s,sentinel = function1(a,b,s,sentinel)
a,b,s,sentinel = function2(a,b,s,sentinel)
a,b,s,sentinel = function3(a,b,s,sentinel)
return a,b,s
Ну, это довольно уродливо.Мы абсолютно ничего не знаем о том, из каких источников берется каждая функция, и мы ничего не знаем о том, как эти функции могут влиять на переменные a
, b
и sentinel
, а также о «любом другом состоянии», которое я просто смоделировал какs
.
Итак, давайте сделаем несколько предположений.Во-первых, я собираюсь предположить, что эти функции напрямую не зависят и не влияют каким-либо образом на значения a
, b
и sentinel
.Они могут, однако, изменить «другое состояние».Итак, вот что мы получаем:
s = initState
sentinel = true
while (sentinel):
a,s2 = function1(s)
b,s3 = function2(s2)
sentinel,s4 = function(s3)
s = s4
return a,b,s
Обратите внимание, что я использовал временные переменные s2
, s3
и s4
, чтобы указать изменения, через которые проходит «другое состояние».Время в Хаскеле.Нам нужна функция управления, которая будет вести себя как цикл while
.
myWhile :: s -- an initial state
-> (s -> (Bool, a, s)) -- given a state, produces a sentinel, a current result, and the next state
-> (a, s) -- the result, plus resultant state
myWhile s f = case f s of
(False, a, s') -> (a, s')
(True, _, s') -> myWhile s' f
Теперь, как можно использовать такую функцию?Что ж, учитывая, что у нас есть функции:
function1 :: MyState -> (AType, MyState)
function2 :: MyState -> (BType, MyState)
function3 :: MyState -> (Bool, MyState)
Мы бы сконструировали желаемый код следующим образом:
thatCodeBlockWeAreTryingToSimulate :: MyState -> ((AType, BType), MyState)
thatCodeBlockWeAreTryingToSimulate initState = myWhile initState f
where f :: MyState -> (Bool, (AType, BType), MyState)
f s = let (a, s2) = function1 s
(b, s3) = function2 s2
(sentinel, s4) = function3 s3
in (sentinel, (a, b), s4)
Обратите внимание, насколько это похоже на приведенный не уродливый код, похожий на python.выше.
Чтобы убедиться, что код, который я представил, хорошо напечатан, добавьте function1 = undefined
и т. д. для трех функций, а также в верхней части файла:
{-# LANGUAGE EmptyDataDecls #-}
data MyState
data AType
data BType
Итак, выводное сообщение таково: в Haskell вы должны явно смоделировать изменения в состоянии.Вы можете использовать «Государственную монаду», чтобы сделать вещи немного красивее, но сначала вы должны понять идею передачи состояния вокруг.