Как вы увеличиваете переменную на функциональном языке программирования?
Например, я хочу:
main :: IO ()
main = do
let i = 0
i = i + 1
print i
Ожидаемый результат: 1.
Как вы увеличиваете переменную на функциональном языке программирования?
Например, я хочу:
main :: IO ()
main = do
let i = 0
i = i + 1
print i
Ожидаемый результат: 1.
Простой способ - ввести затенение имени переменной:
main :: IO () -- another way, simpler, specific to monads:
main = do main = do
let i = 0 let i = 0
let j = i i <- return (i+1)
let i = j+1 print i
print i -- because monadic bind is non-recursive
Отпечатки 1
.
Просто написать let я = i+1
не работает, потому что let
в Haskell дает рекурсивные определения - это на самом деле схема letrec
. i
в правой части let я = i+1
относится к i
в левой части, а не к верхнему уровню i
как могло бы быть. Таким образом, мы разбиваем это уравнение, вводя другую переменную, j
.
Другой, более простой способ - использовать монадное связывание, <-
в do
-notation. Это возможно, потому что монадное связывание не является рекурсивным.
В обоих случаях мы вводим новую переменную под тем же именем, таким образом "затеняя" старую сущность, то есть делая ее более недоступной.
Здесь нужно понять одну вещь: функциональное программирование с чистыми неизменяемыми значениями (как у нас в Haskell) заставляет нас явно указывать время в нашем коде.
В императивной обстановке время подразумевается. Мы "меняем" наши переменные, но любое изменение является последовательным. Мы никогда не сможем изменить то, что этот вар был минуту назад - только то, что будет теперь.
В чисто функциональном программировании это просто делается явным. Одна из самых простых форм, которые это может принять, - использование списков значений в качестве записей последовательных изменений в императивном программировании. Еще проще использовать разные переменные в целом для представления разных значений объекта в разные моменты времени (см. Статическую форму одиночного назначения или SSA).
Таким образом, вместо того, чтобы "изменить" что-то, что не может быть изменено в любом случае, мы делаем расширенную копию и передаем это, используя вместо старой вещи.
Как правило, у вас нет (и вам не нужно). Однако в интересах полноты.
import Data.IORef
main = do
i <- newIORef 0 -- new IORef i
modifyIORef i (+1) -- increase it by 1
readIORef i >>= print -- print it
Однако любой ответ, в котором говорится, что вам нужно использовать что-то вроде MVar, IORef, STRef и т.д., неверно. Существует чисто функциональный способ сделать это, что в этом маленьком быстро написанном примере на самом деле выглядит не очень хорошо.
import Control.Monad.State
type Lens a b = ((a -> b -> a), (a -> b))
setL = fst
getL = snd
modifyL :: Lens a b -> a -> (b -> b) -> a
modifyL lens x f = setL lens x (f (getL lens x))
lensComp :: Lens b c -> Lens a b -> Lens a c
lensComp (set1, get1) (set2, get2) = -- Compose two lenses
(\s x -> set2 s (set1 (get2 s) x) -- Not needed here
, get1 . get2) -- But added for completeness
(+=) :: (Num b) => Lens a b -> Lens a b -> State a ()
x += y = do
s <- get
put (modifyL x s (+ (getL y s)))
swap :: Lens a b -> Lens a b -> State a ()
swap x y = do
s <- get
let x' = getL x s
let y' = getL y s
put (setL y (setL x s y') x')
nFibs :: Int -> Int
nFibs n = evalState (nFibs_ n) (0,1)
nFibs_ :: Int -> State (Int,Int) Int
nFibs_ 0 = fmap snd get -- The second Int is our result
nFibs_ n = do
x += y -- Add y to x
swap x y -- Swap them
nFibs_ (n-1) -- Repeat
where x = ((\(x,y) x' -> (x', y)), fst)
y = ((\(x,y) y' -> (x, y')), snd)
Существует несколько решений для перевода императивного программирования i=i+1
в функциональное программирование. Рекурсивное функциональное решение является рекомендуемым способом в функциональном программировании, создание состояния почти никогда не является тем, что вы хотите сделать.
Через некоторое время вы узнаете, что можете использовать [1..]
если вам нужен, например, индекс, но требуется много времени и практики, чтобы думать функционально, а не обязательно.
Здесь другой способ сделать что-то похожее, поскольку i=i+1
не идентичен, потому что нет никаких разрушительных обновлений. Обратите внимание, что пример монады State приведен только для иллюстрации, вы, вероятно, хотите вместо него [1..]
:
module Count where
import Control.Monad.State
count :: Int -> Int
count c = c+1
count' :: State Int Int
count' = do
c <- get
put (c+1)
return (c+1)
main :: IO ()
main = do
-- purely functional, value-modifying (state-passing) way:
print $ count . count . count . count . count . count $ 0
-- purely functional, State Monad way
print $ ('evalState' 0) $ do {
count' ; count' ; count' ; count' ; count' ; count' }