Home How do I update the values of these Int refs (Haskell)?

# How do I update the values of these Int refs (Haskell)?

user8930358
1#
user8930358 Published in 2017-11-13 14:58:30Z
 I have some variables like so: let y0 = read (input!!0) :: Int let h = read (input!!1) :: Int y <- newIORef y0 minY <- newIORef 0 maxY <- newIORef (h - 1)  And later I have y_old <- readIORef y if (some_string!!0 == 'U') then maxY = (y_old - 1) --I don't think this is working else if (some_string!!0 == 'D') then minY = (y_old + 1) --I don't think this is working  I am basically reading the references into some local ints and then trying to update the references based on criteria. I also tried modifyIORef maxY (y_old - 1) but this doesn't work either. The compiler is only telling me "parse error" or "syntax error" which is not helpful. My full code: import System.IO import Control.Monad import Data.IORef import Text.Printf main :: IO () main = do hSetBuffering stdout NoBuffering -- DO NOT REMOVE -- Auto-generated code below aims at helping you parse -- the standard input according to the problem statement. input_line <- getLine let input = words input_line let w = read (input!!0) :: Int -- width of the building. let h = read (input!!1) :: Int -- height of the building. input_line <- getLine let n = read input_line :: Int -- maximum number of turns before game over. input_line <- getLine let input = words input_line let x0 = read (input!!0) :: Int let y0 = read (input!!1) :: Int x <- newIORef x0 y <- newIORef y0 minX <- newIORef 0 maxX <- newIORef (w - 1) minY <- newIORef 0 maxY <- newIORef (h - 1) loop x0 y0 w h x y minX maxX minY maxY loop :: Int -> Int -> Int -> Int -> IORef Int -> IORef Int -> IORef Int -> IORef Int-> IORef Int -> IORef Int -> IO () loop x0 y0 w h x y minX maxX minY maxY = do input_line <- getLine let bombdir = input_line :: String -- the direction of the bombs from batman's current location (U, UR, R, DR, D, DL, L or UL) x_old <- readIORef x y_old <- readIORef y if (bombdir!!0 == 'U') then writeIORef maxY (y_old - 1) if (bombdir!!0 == 'D') then writeIORef minY (y_old + 1) if (bombdir!!(bombdir.length-1) == 'L') then writeIORef maxX (x_old - 1) if (bombdir!!(bombdir.length-1) == 'R') then writeIORef minX (x_old + 1) x = (minX + maxX) / 2 y = (minY + maxY) / 2 x_out <- readIORef x y_out <- readIORef y printf "%d %d" x_out y_out loop x0 y0 w h x y minX maxX minY maxY 
 First a bit of good style:  ... input_line <- getLine let input = words input_line let w = read (input!!0) :: Int -- width of the building. let h = read (input!!1) :: Int -- height of the building.  This works, but it's awkward. You build up a list and give it a name, only to access individual elements, parse them one-by-one and give them new names. Why not just  input_line <- getLine let [w,h] = map read (words input_line) :: [Int]  In fact, you don't even need input_line, nor the signatures (provided you later use w and h, so their types are unambiguous). I.e., the following also does the trick:  [w,h] <- map read . words <$> getLine  Likewise,  [x₀,y₀] <- map read . words <$> getLine  The real problem with your code in loop is that you try to use if as a procedural control-flow statement. Haskell doesn't have control flow, it only has expressions. Hence Haskell's if is actually very different from the if statement in imperative languages, it's more like the ? : operator. One thing you could do is complete these conditionals to  if (bombdir!!0 == 'U') then writeIORef maxY (y_old - 1) else pure ()  ...pure () being the no-op in a pseudo-imperative (monadic) do block. But there's a standard combinator for this particular construct, when. It's actually just a library function, but behaves almost exactly like if in imperative languages. With that, you could make it  when (bombdir!!0 == 'U') $writeIORef maxY (y_old - 1)  But I wouldn't recommend it. You're again do awkward list-element accessing. A much nicer way of deciding what to do depending on values of list-elements is directly pattern-matching them with a case expression: loop :: Int -> Int -> Int -> Int -> IORef Int -> IORef Int -> IORef Int -> IORef Int-> IORef Int -> IORef Int -> IO () loop x₀ y₀ w h x y minX maxX minY maxY = do bombdir <- getLine -- no need to first name it input_line x_old <- readIORef x y_old <- readIORef y case bomdir of ('U':_) -> writeIORef maxY (y_old - 1) ('D':_) -> writeIORef minY (y_old + 1) _ -> pure () case reverse bombdir of ('L':_) -> writeIORef maxX (x_old - 1) ('R':_) -> writeIORef minX (x_old + 1) _ -> pure ()  Then, you attempt to do arithmetic directly on IORefs, instead of on pure values. This is generally eschewed; what you'd actually need to write is  writeIORef x =<< do xmin <- readIORef minX xmax <- readIORef maxX pure$ (xmin + xmax) / 2  I think we agree that this looks horrible, but it works. A more concise way of writing it would be  writeIORef x =<< (/2) <$> ((+) <$> readIORef minX <*> readIORef maxX)  As I already commented, you fortunately don't need those ugly IORefs at all. Just make “new versions” of your variables to pass in the recursive call: loop :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int-> Int -> Int -> IO () loop x₀ y₀ w h x y minX maxX minY maxY = do bombdir <- input_line let (maxY',minY') = case bombdir of ('U':_) -> (y - 1, minY ) ('D':_) -> (maxY , y + 1) (maxX',minX') = case reverse bombdir of ('L':_) -> (x - 1, minX ) ('R':_) -> (maxX , x + 1) x' = (minX' + maxX') / 2 y' = (minY' + maxY') / 2 printf "%d %d" x' y' loop x₀ y₀ w h x' y' minX' maxX' minY' maxY'