Подтвердить что ты не робот

Как увеличить переменную в функциональном программировании

Как вы увеличиваете переменную на функциональном языке программирования?

Например, я хочу:

main :: IO ()
main = do
    let i = 0
    i = i + 1
    print i

Ожидаемый результат: 1.

4b9b3361

Ответ 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).

Таким образом, вместо того, чтобы "изменить" что-то, что не может быть изменено в любом случае, мы делаем расширенную копию и передаем это, используя вместо старой вещи.

Ответ 2

Как правило, у вас нет (и вам не нужно). Однако в интересах полноты.

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)

Ответ 3

Существует несколько решений для перевода императивного программирования 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' }