Let’s use the following function as an example:
<code>findWord :: [[Char]] -> [(Int, Int)] -> (Int, Int) -> String -> Bool
findWord _ _ _ [] = True -- Word was found
findWord xs ys (row, col) (z : zs)
| (<) row 0 = False -- Index out of Bounds
| (<) col 0 = False -- Index out of Bounds
| (>=) row (length xs) = False -- Index out of Bounds
| (>=) col (length $ head xs) = False -- Index out of Bounds
| (/=) z $ xs !! row !! col = False -- Letter doesn't match
| (row, col) `elem` ys = False -- Coords already visited
| findWord xs visited (row - 1, col) zs = True -- Check Top
| findWord xs visited (row + 1, col) zs = True -- Check Bottom
| findWord xs visited (row, col - 1) zs = True -- Check Left
| findWord xs visited (row, col + 1) zs = True -- Check Right
where
visited = (row, col) : ys -- Add to visited list
</code>
<code>findWord :: [[Char]] -> [(Int, Int)] -> (Int, Int) -> String -> Bool
findWord _ _ _ [] = True -- Word was found
findWord xs ys (row, col) (z : zs)
| (<) row 0 = False -- Index out of Bounds
| (<) col 0 = False -- Index out of Bounds
| (>=) row (length xs) = False -- Index out of Bounds
| (>=) col (length $ head xs) = False -- Index out of Bounds
| (/=) z $ xs !! row !! col = False -- Letter doesn't match
| (row, col) `elem` ys = False -- Coords already visited
| findWord xs visited (row - 1, col) zs = True -- Check Top
| findWord xs visited (row + 1, col) zs = True -- Check Bottom
| findWord xs visited (row, col - 1) zs = True -- Check Left
| findWord xs visited (row, col + 1) zs = True -- Check Right
where
visited = (row, col) : ys -- Add to visited list
</code>
findWord :: [[Char]] -> [(Int, Int)] -> (Int, Int) -> String -> Bool
findWord _ _ _ [] = True -- Word was found
findWord xs ys (row, col) (z : zs)
| (<) row 0 = False -- Index out of Bounds
| (<) col 0 = False -- Index out of Bounds
| (>=) row (length xs) = False -- Index out of Bounds
| (>=) col (length $ head xs) = False -- Index out of Bounds
| (/=) z $ xs !! row !! col = False -- Letter doesn't match
| (row, col) `elem` ys = False -- Coords already visited
| findWord xs visited (row - 1, col) zs = True -- Check Top
| findWord xs visited (row + 1, col) zs = True -- Check Bottom
| findWord xs visited (row, col - 1) zs = True -- Check Left
| findWord xs visited (row, col + 1) zs = True -- Check Right
where
visited = (row, col) : ys -- Add to visited list
In it, the final three guards could be replaced by (||)
and the middle three = True
deleted to give us the same exact result, with the upside of being slightly less verbose.
I assume, however, that my code wouldn’t be tail-recursive if I did so, even though (||)
is lazily-evaluated in Haskell.
Should I always use guards in such cases, or is GHC smart enough to optimize it? In case that it does not matter, is one way preferable to the other for readability purposes?