haskell - few more monad - solving problem with Monads

阅读更多

We have seen the RPN can be solved with haskell in previous examples, now we will see how we can do use monad to solve it.

With monad,  you might not gain more in terms of just solving hte problem, but you will get more like logging ability or, graceful failure or somehting else. 

 

So, here is a example of how you can leverage the use of Monad to give more context information on the solution. 

 

import Data.List  
  
solveRPN :: String -> Double  
solveRPN = head . foldl foldingFunction [] . words  

 

 

as before this is what we have for the main body of the solution, 

 

 

foldingFunction :: [Double] -> String -> [Double]  
foldingFunction (x:y:ys) "*" = (x * y):ys  
foldingFunction (x:y:ys) "+" = (x + y):ys  
foldingFunction (x:y:ys) "-" = (y - x):ys  
foldingFunction xs numberString = read numberString:xs  

 

 now, we are looking to add graceful failure to the function, and we might change the signature to something like this: 

 

foldingFunction :: [Double] -> String -> Maybe [Double]  

 so we will return Just a new stack if success, otherwise we will return Nothing. 

 

we nees some assiisting functions.  like the readMaybe, 

 

readMaybe :: (Read a) => String -> Maybe a  
readMaybe st = case reads st of [(x,"")] -> Just x  
                                _ -> Nothing  

 

to test that with the interactive console.. 

ghci> readMaybe "1" :: Maybe Int  
Just 1  
ghci> readMaybe "GO TO HELL" :: Maybe Int  
Nothing  

 

so with that , readMaybe can return failure on some input that it is not valid. now, we can rewrite the code .. 

 

foldingFunction :: [Double] -> String -> Maybe [Double]  
foldingFunction (x:y:ys) "*" = return ((x * y):ys)  
foldingFunction (x:y:ys) "+" = return ((x + y):ys)  
foldingFunction (x:y:ys) "-" = return ((y - x):ys)  
foldingFunction xs numberString = liftM (:xs) (readMaybe numberString)  

 

and we will ignore those succeess cases and we will only show the failure case. here is what we might get for a invalid .. 

ghci> foldingFunction [] "1"  
Just [1.0]  
ghci> foldingFunction [] "1 wawawawa"  
Nothing  

 so with this, we will wrap it up and here is waht we can get .. 

import Data.List  
  
solveRPN :: String -> Maybe Double  
solveRPN st = do  
    [result] <- foldM foldingFunction [] (words st)  
    return result  

 so instead of normal foldl, we do a foldM,  so we will give  a shot...

ghci> solveRPN "1 2 * 4 +"  
Just 6.0  
ghci> solveRPN "1 2 * 4 + 5 *"  
Just 30.0  
ghci> solveRPN "1 2 * 4"  
Nothing  
ghci> solveRPN "1 8 wharglbllargh"  
Nothing  

 

 

Now that we have seen that we can solve some problems with monad , like the above we used that to solve the reverseRPN problem, now let's see how ew can compose monadic functions, 

 

first let's review some function composition. 

ghci> let f = (+1) . (*100)  
ghci> f 4  
401  
ghci> let g = (\x -> return (x+1)) <=< (\x -> return (x*100))  
ghci> Just 4 >>= g  
Just 401  

 

so you can see that instead of just . function composition, we use the >=> , but instead of id as the starting accumulator, and the . notatin, we uses "return" and "<=<" insteads. so 

 

ghci> let f = foldr (.) id [(+1),(*100),(+1)]  
ghci> f 1  
201  

 can be translated to somethng else. 

 

so let's divert to something else, rember that we have solved some problem like moveKnight questions. 

in3 start = return start >>= moveKnight >>= moveKnight >>= moveKnight   

 and we can check that if we can reach to some position in three moves 

canReachIn3 :: KnightPos -> KnightPos -> Bool  
canReachIn3 start end = end `elem` in3 start

 with monad, we can know not just three, but any numbers of moves, let's make it more general , here is the code. 

import Data.List  
  
inMany :: Int -> KnightPos -> [KnightPos]  
inMany x start = return start >>= foldr (<=<) return (replicate x moveKnight)  

 

as we know that we have return instead of "id".. 

and we can make it more general , here is what we will get.  

canReachIn :: Int -> KnightPos -> KnightPos -> Bool  
canReachIn x start end = end `elem` inMany x start  

 

 

你可能感兴趣的:(haskell)