Reputation: 1794
Sorry for the title gore (if you can suggest a better, please do). But my problem is that I dont quite understand how to get this do block to work. I have a code that returns the position of 5
in a list of lists. Like such:
findFive :: [[Int]] -> (Int, Int)
findFive rs = do
x <- xPos rs 0
y <- yPos rs 0
return ( (x,y) )
xPos :: [[Int]] -> Int -> Int
xPos (rs:[[]]) n = n
xPos (rs:rss) n | elem 5 rs = n
| otherwise = xPos rss (n+1)
yPos :: [[Int]] -> Int -> Int
yPos (rs:[[]]) n = n
yPos (rs:rss) n | elem 5 rs = n
| otherwise = yPos rss (n+1)
I
But I cant use my do block this way. I can get it to work by doing
findFive :: [[Int]] -> (Int, Int)
findFive xs = ( (xPos xs 0), (yPos (transpose (xs)) 0) )
But that looks kinda ugly.
Also, is there a way to get this to work without sending in 0
to xPos
and yPos
?
Upvotes: 2
Views: 186
Reputation: 531245
Let's say pos
is defined this way:
pos :: Eq => a -- A value to find
-> [[a]] -- A 2d matrix
-> Maybe Int -- Index of first row containing the value, if any
pos k rows = pos' rows 0
where pos' [] _ = Nothing
pos' (x:xs) n | elem k x = n
| otherwise = pos' xs (n+1)
There are several changes here:
Int
.k :: a
, not just 5.k
.With this definition, we could define findFive
as
findFive :: [[Int]] -> (Maybe Int, Maybe Int)
findFive xs = (pos 5 xs, pos 5 (transpose xs))
Using Control.Lens
, you can factor out the function pos 5
so that it only needs to be written once. Think of over both
as a version of map
for pairs instead of lists.
import Control.Lens
findFive xs = over both (pos 5) (xs, transpose xs)
Using Control.Arrow
, you can factor out the argument xs
so that it only needs to be written once.
import Control.Lens
import Control.Arrow
findFive xs = over both (pos 5) ((id &&& transpose) xs)
-- id &&& transpose = \x -> (id x, transpose x)
Once you've done that, you can easily write findFive
in point-free style, by composing over both (pos 5)
and id &&& transpose
:
findFive = over both (pos 5) . (id &&& transpose)
Upvotes: 0
Reputation: 11607
You can't use a do
block this way because in do
blocks you have to (1) bind names to the 'contents' of monadic values, and (2) return a value wrapped in the same monadic type as used in (1). In this case the monadic type would be the list. It's appropriate to return a list of (row, column) pairs because that automatically handles both the cases of not finding the number, or finding it multiple times. So we could do something like
import Control.Monad
findFive ::
[[Int]] -> -- ^ a matrix of numbers.
[(Int, Int)] -- ^ the (zero-indexed) rows and columns of the number
-- ^ @5@, if any (otherwise empty list).
findFive xss =
do
(xs, rowIdx) <- xss `zip` [0 ..]
(x, colIdx) <- xs `zip` [0 ..]
guard $ x == 5
return (rowIdx, colIdx)
input :: [[Int]]
input = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
-- Should print "row: 1, col: 1".
main :: IO ()
main =
forM_ (findFive input) $ \(rowIdx, colIdx) ->
do
putStr "row: "
putStr $ show rowIdx
putStr ", col: "
print colIdx
Upvotes: 0
Reputation: 116139
Why do
? There are no monads there. A let..in
suffices:
findFive :: [[Int]] -> (Int, Int)
findFive rs = let
x = xPos rs 0
y = yPos rs 0
in (x,y)
Alternatively, use where
:
findFive :: [[Int]] -> (Int, Int)
findFive rs = (x, y)
where
x = xPos rs 0
y = yPos rs 0
Upvotes: 8