how to increment a variable in functional programming

后端 未结 4 1633
逝去的感伤
逝去的感伤 2020-11-29 07:45

How do you increment a variable in a functional programming language?

For example, I want to do:

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


        
相关标签:
4条回答
  • 2020-11-29 07:59

    Simple way is to introduce shadowing of a variable name:

    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
    

    Prints 1.

    Just writing let i = i+1 doesn't work because let in Haskell makes recursive definitions — it is actually Scheme's letrec. The i in the right-hand side of let i = i+1 refers to the i in its left hand side — not to the upper level i as might be intended. So we break that equation up by introducing another variable, j.

    Another, simpler way is to use monadic bind, <- in the do-notation. This is possible because monadic bind is not recursive.

    In both cases we introduce new variable under the same name, thus "shadowing" the old entity, i.e. making it no longer accessible.

    How to "think functional"

    One thing to understand here is that functional programming with pure — immutable — values (like we have in Haskell) forces us to make time explicit in our code.

    In imperative setting time is implicit. We "change" our vars — but any change is sequential. We can never change what that var was a moment ago — only what it will be from now on.

    In pure functional programming this is just made explicit. One of the simplest forms this can take is with using lists of values as records of sequential change in imperative programming. Even simpler is to use different variables altogether to represent different values of an entity at different points in time (cf. single assignment and static single assignment form, or SSA).

    So instead of "changing" something that can't really be changed anyway, we make an augmented copy of it, and pass that around, using it in place of the old thing.

    0 讨论(0)
  • 2020-11-29 08:09

    As a general rule, you don't (and you don't need to). However, in the interests of completeness.

    import Data.IORef
    main = do
        i <- newIORef 0       -- new IORef i
        modifyIORef i (+1)    -- increase it by 1
        readIORef i >>= print -- print it
    

    However, any answer that says you need to use something like MVar, IORef, STRef etc. is wrong. There is a purely functional way to do this, which in this small rapidly written example doesn't really look very nice.

    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)
    
    0 讨论(0)
  • 2020-11-29 08:15

    There are several solutions to translate imperative i=i+1 programming to functional programming. Recursive function solution is the recommended way in functional programming, creating a state is almost never what you want to do.

    After a while you will learn that you can use [1..] if you need a index for example, but it takes a lot of time and practice to think functionally instead of imperatively.

    Here's a other way to do something similar as i=i+1 not identical because there aren't any destructive updates. Note that the State monad example is just for illustration, you probably want [1..] instead:

    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' } 
    
    0 讨论(0)
  • 2020-11-29 08:19

    Note: This is not an ideal answer but hey, sometimes it might be a little good to give anything at all.

    A simple function to increase the variable would suffice.

    For example:

    incVal :: Integer -> Integer
    incVal x = x + 1
    
    main::IO()
    main = do
       let i  = 1
       print (incVal i)
    

    Or even an anonymous function to do it.

    0 讨论(0)
提交回复
热议问题