Reputation: 59
I am fairly new to Haskell and trying to comprehend writing functions and if else conditions and everything else. I am trying to write a very basic function but I don't fully understand if-then-else usage. I have a maze that i represent as [[Char]]. And this function will simply look at the position x,y in the maze and returns if it's a valid position or not. (whether it's in the maze boundaries or not)
I have written this so far:
is_valid_position :: Int -> Int -> [[Char]] -> Bool
is_valid_position x y maze
if(x < 0 || y < 0)
then False
if(x >= length maze || y >= length (maze!!0))
then False
else True
This gives an error because of the 'else' usage right now. What I'm trying to write in python is like this:
def is_valid_position(maze, pos_r, pos_c):
if pos_r < 0 or pos_c < 0:
return False
if pos_r >= len(maze) or pos_c >= len(maze[0]):
return False
return True
How should I change my Haskell code? I appreciate any help.
Upvotes: 3
Views: 652
Reputation: 1787
Chepner's answer is excellent. But the "true Haskell" way of doing things would be a little bit different.
First, we define
(!!?) :: [a] -> Int -> Maybe a
[] !!? _ = Nothing
(x:xs) !!? n = if n == 0
then Just x
else xs !!? n - 1
Then, we define
charAtPosition :: Int -> Int -> [[a]] -> Maybe a
charAtPosition x y maze = if x < 0 || y < 0
then Nothing else
case maze !!? x of
Nothing -> Nothing
Just column -> column !!? y
charAtPosition x y maze
returns the character at position x, y
if there is such a character. Otherwise, it returns 0. To check whether indices x
and y
are valid, we simply say
isValidPosition x y maze = isJust (charAtPosition x y maze)
(where isJust
is imported from Data.Maybe).
Note that we can actually clean up charAtPosition
using the power of monad notation. Haskell defines the following function:
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
w >>= f = case w of
Nothing -> Nothing
Just k -> f k
Using >>=
, we can thus rewrite charAtPosition x y maze
to be
charAtPosition x y maze = if x < 0 || y < 0
then Nothing
else (maze !!? x) >>= (!!? y)
Another common piece of monad notation is >>
. In this case, >>
is defined by
(>>) :: Maybe a -> Maybe b -> Maybe b
a >> b = case a of
Nothing -> Nothing
Just c -> b
The last piece of the puzzle is the guard
function, defined in Control.Monad. In this case, the definition is
guard :: Bool -> Maybe ()
guard bool = if bool
then Just ()
else Nothing
With the pieces of the puzzle, we can write
charAtPosition x y maze = guard (x >= 0 && y >= 0) >> (maze !!? x) >>= (!!? y)
We can make this slightly nicer using do
notation to get
charAtPosition x y maze = do guard (x >= 0 && y >= 0)
(maze !!? x) >>= (!!? y)
which is the final Haskell way to write charAtPosition
. Informally, we can read the definition of charAtPosition x y maze
as saying: first, make sure that x >= 0
and that y >= 0
. Then, attempt to look up the x
th element of maze
. Finally, attempt to look up the y
th element of the result.
Upvotes: 2
Reputation: 530892
The if-else
expression requires both parts. You can nest the expressions, so something like if c1 then a else if c2 then b else c
.
is_valid_position x y maze = if (x < 0 || y > 0)
then False
else if (x >= length maze | y >= length (maze !! 0)
then False
else True
However, you don't need if-else
expression here, since you are working purely with Boolean values. You can simply use &&
and ||
.
-- Instead of prohibiting any condition to be true,
-- require each negated condition to be true
is_valid_position x y maze = x >= 0
&& y >= 0
&& x < length maze
&& y < length (maze !! 0)
Guard conditions are also an option to break it up a little bit:
is_valid_position x y maze | x < 0 = False
| y < 0 = False
| x >= length maze = False
| y >= length (maze !! 0) = False
| otherwise = True
Upvotes: 4