patternMinor
Solve a maze in the form of a 2D array using BFS - Haskell
Viewed 0 times
thearraymazebfssolveusinghaskellform
Problem
Suggestions for improving coding style are greatly appreciated.
```
import qualified Data.List as L
import qualified Data.Map.Strict as M
import qualified Data.Vector as V
type Queue a = ([a], [a])
emptyQueue = ([], [])
pushListToAnother fromLst toLst = L.foldl' (\ys x -> (x:ys)) toLst fromLst
enqueue :: Queue a -> a -> Queue a
enqueue (inList, outList) x = ((x:inList), outList)
dequeue :: Queue a -> Maybe (a, Queue a)
dequeue (inList, outList) = case outList of
(y:ys) -> Just (y, (inList, ys))
[] -> if (null inList) then Nothing else dequeue ([], reverse inList)
massEnqueue :: Queue a -> [a] -> Queue a
massEnqueue (inList, outList) items = ((pushListToAnother items inList), outList)
-- consider moving the above Queue code into a separate module.
type Grid a = V.Vector (V.Vector a)
type Indices = (Int, Int)
access grid (x, y) = (grid V.! x) V.! y
massInsert :: Ord k => [(k, v)] -> M.Map k v -> M.Map k v
massInsert elems theMap = L.foldl' (\m (k, v) -> M.insert k v m) theMap elems
validAndTraversable :: (a -> Bool) -> Grid a -> Indices -> Bool
validAndTraversable traversability grid xy@(x, y) = let xbound = V.length grid in
let ybound = V.length (V.head grid) in
let withinBounds = (x >= 0) && (x = 0) && (y M.Map a a -> a -> a -> [a]
getPath visitedFromMap start current = pathHelper visitedFromMap start current []
where pathHelper prevIndicesMap start current path = let newPath = (current:path) in
if current == start
then newPath
else case (M.lookup current prevIndicesMap) of
Nothing -> []
```
import qualified Data.List as L
import qualified Data.Map.Strict as M
import qualified Data.Vector as V
type Queue a = ([a], [a])
emptyQueue = ([], [])
pushListToAnother fromLst toLst = L.foldl' (\ys x -> (x:ys)) toLst fromLst
enqueue :: Queue a -> a -> Queue a
enqueue (inList, outList) x = ((x:inList), outList)
dequeue :: Queue a -> Maybe (a, Queue a)
dequeue (inList, outList) = case outList of
(y:ys) -> Just (y, (inList, ys))
[] -> if (null inList) then Nothing else dequeue ([], reverse inList)
massEnqueue :: Queue a -> [a] -> Queue a
massEnqueue (inList, outList) items = ((pushListToAnother items inList), outList)
-- consider moving the above Queue code into a separate module.
type Grid a = V.Vector (V.Vector a)
type Indices = (Int, Int)
access grid (x, y) = (grid V.! x) V.! y
massInsert :: Ord k => [(k, v)] -> M.Map k v -> M.Map k v
massInsert elems theMap = L.foldl' (\m (k, v) -> M.insert k v m) theMap elems
validAndTraversable :: (a -> Bool) -> Grid a -> Indices -> Bool
validAndTraversable traversability grid xy@(x, y) = let xbound = V.length grid in
let ybound = V.length (V.head grid) in
let withinBounds = (x >= 0) && (x = 0) && (y M.Map a a -> a -> a -> [a]
getPath visitedFromMap start current = pathHelper visitedFromMap start current []
where pathHelper prevIndicesMap start current path = let newPath = (current:path) in
if current == start
then newPath
else case (M.lookup current prevIndicesMap) of
Nothing -> []
Solution
Some general ideas:
-
Some of the
-
Strictly adhere to having some maximum number of columns (often people use 72, 78 or 80). Code that goes too far to the right is very much unreadable.
-
Instead of
-
Prefer guards over
-
Avoid code repetition, for example multiple calls to the same function, if you can factor the call out:
-
Use hlint, it'll give you a lot of useful hints.
- Always give types to top level functions. It improves readability of your code very much.
- Unless you really need to, it's better to use existing data structures than inventing your own. Instead of using
Queue, you could as well useData.Sequence.
-
Some of the
Queue functions can be simplified, often using first from Control.Arrow:pushListToAnother :: [a] -> [a] -> [a]
pushListToAnother fromLst = (reverse fromLst ++)
enqueue :: Queue a -> a -> Queue a
enqueue q x = first (x :) q
massEnqueue :: Queue a -> [a] -> Queue a
massEnqueue q items = first (pushListToAnother items) q-
Strictly adhere to having some maximum number of columns (often people use 72, 78 or 80). Code that goes too far to the right is very much unreadable.
-
Instead of
let f = foo in let g = bar in boo use just let f = foo ; g = bar in boo. For example:validAndTraversable :: (a -> Bool) -> Grid a -> Indices -> Bool
validAndTraversable traversability grid xy@(x, y) =
let xbound = V.length grid
ybound = V.length (V.head grid)
withinBounds = (x >= 0) && (x = 0) && (y < ybound)
in withinBounds && (traversability (access grid xy))-
Prefer guards over
if/then/else. Usually it leads to more concise code and it's more idiomatic. Pattern guards can be even more helpful.getPath :: Ord a => M.Map a a -> a -> a -> [a]
getPath visitedFromMap start current =
pathHelper visitedFromMap start current []
where
pathHelper prevIndicesMap start current path
| current == start
= newPath
| Just e <- M.lookup current prevIndicesMap
= (pathHelper prevIndicesMap start e) $! newPath
| otherwise
= []
where newPath = (current:path)-
Avoid code repetition, for example multiple calls to the same function, if you can factor the call out:
maze1 = V.fromList . map V.fromList $
[ [1,1,1,1,1,1,0]
, [0,0,0,0,0,0,0]
, [1,1,1,1,1,1,0]
, [0,0,0,0,0,0,0]
, [0,1,1,1,1,1,1]
, [0,0,0,0,0,0,0]
, [1,1,1,0,1,1,1]
, [0,0,0,0,0,0,0]
, [0,1,1,1,1,1,0]
]-
Use hlint, it'll give you a lot of useful hints.
Code Snippets
pushListToAnother :: [a] -> [a] -> [a]
pushListToAnother fromLst = (reverse fromLst ++)
enqueue :: Queue a -> a -> Queue a
enqueue q x = first (x :) q
massEnqueue :: Queue a -> [a] -> Queue a
massEnqueue q items = first (pushListToAnother items) qvalidAndTraversable :: (a -> Bool) -> Grid a -> Indices -> Bool
validAndTraversable traversability grid xy@(x, y) =
let xbound = V.length grid
ybound = V.length (V.head grid)
withinBounds = (x >= 0) && (x < xbound) && (y >= 0) && (y < ybound)
in withinBounds && (traversability (access grid xy))getPath :: Ord a => M.Map a a -> a -> a -> [a]
getPath visitedFromMap start current =
pathHelper visitedFromMap start current []
where
pathHelper prevIndicesMap start current path
| current == start
= newPath
| Just e <- M.lookup current prevIndicesMap
= (pathHelper prevIndicesMap start e) $! newPath
| otherwise
= []
where newPath = (current:path)maze1 = V.fromList . map V.fromList $
[ [1,1,1,1,1,1,0]
, [0,0,0,0,0,0,0]
, [1,1,1,1,1,1,0]
, [0,0,0,0,0,0,0]
, [0,1,1,1,1,1,1]
, [0,0,0,0,0,0,0]
, [1,1,1,0,1,1,1]
, [0,0,0,0,0,0,0]
, [0,1,1,1,1,1,0]
]Context
StackExchange Code Review Q#41503, answer score: 4
Revisions (0)
No revisions yet.