I'm really struggling with Haskell atm.
It took me almost 6 hours to write a function that does what I want. Unfortunately I'm not satisfied with the look of it.
Could someone please give me any hints how to rewrite it?
get_connected_area :: Eq generic_type => [[generic_type]] -> (Int, Int) -> [(Int,Int)] -> generic_type -> [(Int,Int)]
get_connected_area habitat point area nullValue
| elem point area = area
| not ((fst point) >= 0) = area
| not ((snd point) >= 0) = area
| not ((fst point) < (length habitat)) = area
| not ((snd point) < (length (habitat!!0))) = area
| (((habitat!!(fst point))!!(snd point))) == nullValue = area
| otherwise =
let new_area = point : area
in
get_connected_area habitat (fst point+1, snd point) (
get_connected_area habitat (fst point-1, snd point) (
get_connected_area habitat (fst point, snd point+1) (
get_connected_area habitat (fst point, snd point-1) new_area nullValue
) nullValue
) nullValue
) nullValue
The function get's a [[generic_type]] (representing a landscape-map) and searches the fully connected area around a point that isn't equal to the given nullValue.
Eg.:
If the function gets called like this:
get_connected_area [[0,1,0],[1,1,1],[0,1,0],[1,0,0]] (1,1) [] 0
That literally means
0 1 0
1 1 1
0 1 0
1 0 0
Represents a map (like google maps). Start from the point (coordinates) (1,1) I want to get all coordinates of the elements that form a connected area with the given point.
The result therefore should be:
0 1 0
1 1 1
0 1 0
1 0 0
And the corresponting return value (list of coordinates of bold 1s):
[(2,1),(0,1),(1,2),(1,0),(1,1)]
One small change is that you can use pattern matching for the variable point. This means you can use (x, y) instead of point in the function declaration:
get_connected_area habitat (x, y) area nullValue = ...
Now everywhere you have fst point, just put x, and everywhere you have snd point, put y.
Another modification is to use more variables for subexpressions. This can help with the nested recursive calls. For example, make a variable for the inner-most nested call:
....
where foo = get_connected_area habitat (x, y-1) new_area nullValue
Now just put foo instead of the call. This technique can now be repeated for the "new" inner-most call. (Note that you should pick a more descriptive name than foo. Maybe down?)
Note that not (x >= y) is the same as x < y. Use this to simplify all of the conditions. Since these conditions test if a point is inside a bounding rectangle, most of this logic can be factored to a function isIn :: (Int, Int) -> (Int, Int) -> (Int, Int) -> Bool which will make get_connected_area more readable.
This would be my first quick pass through the function, and sort of the minimum that might pass a code review (just in terms of style):
getConnectedArea :: Eq a => [[a]] -> a -> (Int, Int) -> [(Int,Int)] -> [(Int,Int)]
getConnectedArea habitat nullValue = go where
go point#(x,y) area
| elem point area = area
| x < 0 = area
| y < 0 = area
| x >= length habitat = area
| y >= length (habitat!!0) = area
| ((habitat!!x)!!y) == nullValue = area
| otherwise =
foldr go (point : area)
[ (x+1, y), (x-1, y), (x, y+1), (x, y-1) ]
We bind habitat and nullValue once at the top level (clarifying what the recursive work is doing), remove indirection in the predicates, use camel-case (underdashes obscure where function application is happening), replace generic_type with a (using a noisy variable here actually has the opposite effect from the one you intended; I end up trying to figure out what special semantics you're trying to call out when the interesting thing is that the type doesn't matter (so long as it can be compared for equality)).
At this point there are lots of things we can do:
pretend we're writing real code and worry about asymptotics of treating lists as arrays (!!, and length) and sets (elem), and use proper array and set data structures instead
move your bounds checking (and possible null value checking) into a new lookup function (the goal being to have only a single ... = area clause if possible
consider improvements to the algorithm: can we avoid recursively checking the cell we just came from algorithmically? can we avoid passing area entirely (making our search nicely lazy/"productive")?
Here is my take:
import qualified Data.Set as Set
type Point = (Int, Int)
getConnectedArea :: (Point -> Bool) -> Point -> Set.Set Point
getConnectedArea habitat = \p -> worker p Set.empty
-- \p is to the right of = to keep it out of the scope of the where clause
where
worker p seen
| p `Set.member` seen = seen
| habitat p = foldr worker (Set.insert p seen) (neighbors p)
| otherwise = seen
neighbors (x,y) = [(x-1,y), (x+1,y), (x,y-1), (x,y+1)]
What I've done
foldr over the neighbors, as some commenters suggested.
Since the order of points doesn't matter, I use a Set instead of a list, so it's semantically a better fit and faster to boot.
Named some helpful intermediate abstractions such as Point and neighbors.
A better data structure for the habitat would also be good, since lists are linear time to access, maybe a 2D Data.Array—but as far as this function cares, all you need is an indexing function Point -> Bool (out of bounds and null value are treated the same), so I've replaced the data structure parameter with the indexing function itself (this is a common transformation in FP).
We can see that it would also be possible to abstract away the neighbors function and then we would arrive at a very general graph traversal method
traverseGraph :: (Ord a) => (a -> [a]) -> a -> Set.Set a
in terms of which you could write getConnectedArea. I recommend doing this for educational purposes—left as an exercise.
EDIT
Here's an example of how to call the function in terms of (almost) your old function:
import Control.Monad ((<=<))
-- A couple helpers for indexing lists.
index :: Int -> [a] -> Maybe a
index _ [] = Nothing
index 0 (x:_) = x
index n (_:xs) = index (n-1) xs
index2 :: (Int,Int) -> [[a]] -> Maybe a
index2 (x,y) = index x <=< index y
-- index2 uses Maybe's monadic structure, and I think it's quite pretty.
-- But if you're not ready for that, you might prefer
index2' (x,y) xss
| Just xs <- index y xss = index x xs
| otherwise = Nothing
getConnectedArea' :: (Eq a) => [[a]] -> Point -> a -> [a]
getConnectedArea' habitat point nullValue = Set.toList $ getConnectedArea nonnull point
where
nonnull :: Point -> Bool
nonnull p = case index2 p habitat of
Nothing -> False
Just x -> x /= nullValue
OK i will try to simplify your code. However there are already good answers and that's why i will tackle this with a slightly more conceptual approach.
I believe you could chose better data types. For instance Data.Matrix seems to provide an ideal data type in the place of your [[generic_type]] type. Also for coordinates i wouldn't chose a tuple type since tuple type is there to pack different types. It's functor and monad instances are not very helpful when it is chosen as a coordinate system. Yet since it seems Data.Matrix is just happy with tuples as coordinates i will keep them.
OK your rephrased code is as follows;
import Data.Matrix
gca :: Matrix Int -> (Int, Int) -> Int -> [(Int,Int)]
gca fld crd nil = let nbs = [id, subtract 1, (+1)] >>= \f -> [id, subtract 1, (+1)]
>>= \g -> return (f,g)
>>= \(f,g) -> return ((f . fst) crd, (g . snd) crd)
in filter (\(x,y) -> fld ! (x,y) /= nil) nbs
*Main> gca (fromLists [[0,1,0],[1,1,1],[0,1,0],[1,0,0]]) (2,2) 0
[(2,2),(2,1),(2,3),(1,2),(3,2)]
The first thing to note is, the Matrix data type is index 1 based. So we have our center point at (2,2).
The second is... we have a three element list of functions defined as [id, subtract 1, (+1)]. The contained functions are all Num a => a -> a type and i need them to define the surrounding pixels of the given coordinate including the given coordinate. So we have a line just like if we did;
[1,2,3] >>= \x -> [1,2,3] >>= \y -> return [x,y] would result [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3]] which, in our case, would yield a 2 combinations of all functions in the place of the numbers 1,2 and 3.
Which then we apply to our given coordinate one by one with a cascading instruction
>>= \[f,g] -> return ((f . fst) crd, (g . snd) crd)
which yields all neighboring coordinates.
Then its nothing more than filtering the neighboring filters by checking if they are not equal to the nil value within out matrix.
Related
deadNeighbors:
Give a list of empty cells that have a living cellular neighbor in that generation. Make sure that each cell is listed only once!
Something not work with my deadNeighbors function, but I do not know what is wrong about it. Can anybody help me to fix deadNeighbors ?
type Coordinate = (Integer, Integer)
type Generation = [Coordinate]
single :: Generation
single = [ (42, 42) ]
row :: Generation
row = [ (10, 1), (10, 2), (10, 3) ]
Code:
neighbors :: Coordinate -> [Coordinate]
neighbors (x,y) = [(x-1,y-1), (x-1,y), (x-1,y+1), (x ,y-1), (x,y+1), (x+1,y-1), (x+1,y), (x+1,y+1)]
alive :: Generation -> Coordinate -> Bool
alive x y = elem y x
livingNeighbors :: Generation -> Coordinate -> Int
livingNeighbors a = length .filter (alive a) . neighbors
staysAlive :: Generation -> Coordinate -> Bool
staysAlive a b
| alive a b = livingNeighbors a b `elem` [2,3]
| otherwise = livingNeighbors a b `elem` [3]
Problems:
deadNeighbors :: Generation -> [Coordinate]
deadNeighbors (neighbors (x,y))
|(alive (x,y)) = Nothing
|otherwise = [] ++ (x,y)
Examples:
sort (deadNeighbors single) == sort [(41.41), (41.42), (41.43), (42.41), (42.43), (43.41), (43.42) , (43.43)]
sort (deadNeighbors row) == sort [(9.0), (9.1), (9.2), (10.0), (11.0), (11.1), (11.2) , (9.3), (11.3), (9.4), (10.4), (11.4)]
Let's go over deadNeighbors function line by line:
(the type signature looks fine)
deadNeighbors (neighbors (x,y))
This is a clause of the deadNeighbors function and seems to use pattern matching notation, however you use the function neighbors in the pattern match. That is not allowed. I don't really know what your intention is here, so I cannot suggest a way to fix this.
|(alive (x,y)) = Nothing
Here you are using a guard correctly, but the alive function requires the generation in addition to the coordinate. You should pass both as arguments here.
Also, you are returning Nothing which has type Maybe a for some a. But the signature of the deadNeighbors function indicates it should return a [Coordinate]. Perhaps you intended to write [] (the empty list)?
|otherwise = [] ++ (x,y)
Here you are using the ++ operator, which expects two lists as arguments, with an empty list [] as argument and a coordinate (x,y). The coordinate type is not a list, so it is not compatible with the ++ operator. Perhaps you intended (x,y) : [] or just [(x,y)] (which means the same thing but is slightly prettier)?
I want to add two positive numbers together without the use of any basic operators like + for addition. I've already worked my way around that (in the add''' function) (i think) may not be efficient but thats not the point right now. I am getting lots of type errors however which i have no idea how to handle, and is very confusing for me as it works on paper and i've come from python.
add 1245 7489
--add :: Int -> Int -> Int
add x y = add'' (zip (add' x) (add' y))
where
add' :: Int -> [Int]
add' 0 = []
add' x = add' (x `div` 10) ++ [x `mod` 10]
conversion [1,2,4,5] [7,4,8,9] then zipping them together [(1,7),(2,4)....]
add'' :: [(Int,Int)] -> [Int]
add'' (x:xs) = [(add''' (head x) (last x))] ++ add'' xs
summary [8,6,...] what happens when the sum reaches 10 is not implemented yet.
where
--add''' :: (Int,Int) -> Int
add''' x y = last (take (succ y) $ iterate succ x)
adding two numbers together
You can't use head and last on tuples. ...Frankly, you should never use these functions at all because they're unsafe (partial), but they can be used on lists. In Haskell, lists are something completely different from tuples.To get at the elements of a tuple, use pattern matching.
add'' ((x,y):xs) = [add''' x y] ++ add'' xs
(To get at the elements of a list, pattern matching is very often the best too.) Alternatively, you can use fst and snd, these do on 2-tuples what you apparently thought head and last would.
Be clear which functions are curried and which aren't. The way you write add''', its type signature is actually Int -> Int -> Int. That is equivalent to (Int, Int) -> Int, but it's still not the same to the type checker.
The result of add'' is [Int], but you're trying to use this as Int in the result of add. That can't work, you need to translate from digits to numbers again.
add'' doesn't handle the empty case. That's fixed easily enough, but better than doing this recursion at all is using standard combinators. In your case, this is only supposed to work element-wise anyway, so you can simply use map – or do that right in the zipping, with zipWith. Then you also don't need to unwrap any tuples at all, because it works with a curried function.
A clean version of your attempt:
add :: Int -> Int -> Int
add x y = fromDigits 0 $ zipWith addDigits (toDigits x []) (toDigits y [])
where
fromDigits :: Int -> [Int] -> Int
fromDigits acc [] = acc
fromDigits acc (d:ds)
= acc `seq` -- strict accumulator, to avoid thunking.
fromDigits (acc*10 + d) ds
toDigits :: Int -> [Int] -> [Int] -- yield difference-list,
toDigits 0 = id -- because we're consing
toDigits x = toDigits (x`div`10) . ((x`mod`10):) -- left-associatively.
addDigits :: Int -> Int -> Int
addDigits x y = last $ take (succ x) $ iterate succ y
Note that zipWith requires both numbers to have the same number of digits (as does zip).
Also, yes, I'm using + in fromDigits, making this whole thing pretty futile. In practice you would of course use binary, then it's just a bitwise-or and the multiplication is a left shift. What you actually don't need to do here is take special care with 10-overflow, but that's just because of the cheat of using + in fromDigits.
By head and last you meant fst and snd, but you don't need them at all, the components are right there:
add'' :: [(Int, Int)] -> [Int]
add'' (pair : pairs) = [(add''' pair)] ++ add'' pairs
where
add''' :: (Int, Int) -> Int
add''' (x, y) = last (take (succ y) $ iterate succ x)
= iterate succ x !! y
= [x ..] !! y -- nice idea for an exercise!
Now the big question that remains is what to do with those big scary 10-and-over numbers. Here's a thought: produce a digit and a carry with
= ([(d, 0) | d <- [x .. 9]] ++ [(d, 1) | d <- [0 ..]]) !! y
Can you take it from here? Hint: reverse order of digits is your friend!
the official answer my professor gave
works on positive and negative numbers too, but still requires the two numbers to be the same length
add 0 y = y
add x y
| x>0 = add (pred x) (succ y)
| otherwise = add (succ x) (pred y)
The other answers cover what's gone wrong in your approach. From a theoretical perspective, though, they each have some drawbacks: they either land you at [Int] and not Int, or they use (+) in the conversion back from [Int] to Int. What's more, they use mod and div as subroutines in defining addition -- which would be okay, but then to be theoretically sound you would want to make sure that you could define mod and div themselves without using addition as a subroutine!
Since you say efficiency is no concern, I propose using the usual definition of addition that mathematicians give, namely: 0 + y = y, and (x+1) + y = (x + y)+1. Here you should read +1 as a separate operation than addition, a more primitive one: the one that just increments a number. We spell it succ in Haskell (and its "inverse" is pred). With this theoretical definition in mind, the Haskell almost writes itself:
add :: Int -> Int -> Int
add 0 y = y
add x y = succ (add (pred x) y)
So: compared to other answers, we can take an Int and return an Int, and the only subroutines we use are ones that "feel" more primitive: succ, pred, and checking whether a number is zero or nonzero. (And we land at only three short lines of code... about a third as long as the shortest proposed alternative.) Of course the price we pay is very bad performance... try add (2^32) 0!
Like the other answers, this only works for positive numbers. When you are ready for handling negative numbers, we should chat again -- there's some fascinating mathematical tricks to pull.
I want to define a simple function in Haskell:
nzp :: [Int] -> (Int,Int,Int)
that accepts a list of integers as input and returns a triple (a,b,c) where a is the amount of numbers in the list less than 0, b is the amount equal to 0 and c is the amount higher than zero. For example,
nzp [3,0,-2,0,4,5] = (1,2,3)
I have to define this function recursively and I can only traverse the list once. How can I do this? I can't seem to grasp the concept of recursively creating a tuple.
Most Regards
Here are some pointers:
To use recursion to build up a value, you need to pass the previous version of the value as an argument, so write
nzp_helper :: [Int] -> (Int,Int,Int) -> (Int, Int, Int)
Decide what the answer is when the list is empty
nzp_helper [] runningTotals = -- what's the answer if the list is empty?
Decide what the answer is when there's something in the list
nzp_helper (i:is) (negatives, positives, zeros) =
| i < 0 = -- write stuff here
| i == 0 = -- I hope you're getting the idea
Kick the whole thing off by defining nzp using nzp_helper.
nzp is = nzp_helper is -- one final argument - what?
Instead of thinking of trying to create a single tuple recursively, you should think about updating an existing tuple containing the counts based on given value. This function would look something like:
update :: (Int, Int, Int) -> Int -> (Int, Int, Int)
update (l,e,g) x | x < 0 = (l+1, e, g)
update (l,e,g) x | x == 0 = (l, e+1, g)
update (l,e,g) x | x > 0 = (l, e, g+1)
Then you can traverse the input list using foldl and accumulate the output tuple:
nzp :: [Int] -> (Int, Int, Int)
nzp = foldl update (0,0,0)
EDIT: As #leftroundabout points out in the comments, you should avoid using foldl since it can lead to space leaks - you can find an explanation in Real World Haskell. You can use the strict version of foldl, foldl' in Data.List
import Data.List
nzp = foldl' update (0,0,0)
I can't seem to grasp the concept of recursively creating a tuple.
I shouldn't think so – in fact, it's impossible! (At least not without GHC extension havoc.)
No, you need to create a tuple in one go. The important thing needed for your function: since you may only traverse the list once, you need to pull through the entire tuple at once as well. As this is apparently homework, I shall instead show how it works with a fold (that is in fact preferrable, but translates quite directly to recursion):
nzp = foldr switchIncr (0,0,0)
where switchIncr x (negatives, zeroes, positives)
| x<0 = (succ negatives, zeroes, positives)
| x==0 = (negatives, succ zeroes, positives)
| otherwise = (negatives, zeroes, succ positives)
New to Haskell too! Perhaps not proper but here is my solution which works.
Define an auxiliary function that accumulates the n,z, and p values
let f (x:xs, (n, z, p)) | x < 0 = f (xs, (n+1, z, p))
| x == 0 = f (xs, (n, z+1, p))
| otherwise = f (xs, (n, z, p+1))
f ([], (n, z, p)) = ([], (n, z, p))
and define nzp in terms of the auxiliary function
let nzp x = snd $ f (x,(0,0,0))
to verify
Prelude> nzp [-1,1,1,-1,0]
(2,1,2)
I am a little over a couple of months into Haskell. A helper/auxilliary function would make running this solution easier.
s3 [] (l,g,z) = (l,g,z)
s3 (x:xs) (l,g,z) = if x<0
then (s3 xs (l+1,g,z))
else if x>0
then (s3 xs (l,g+1,z))
else (s3 xs (l,g,z+1))
This is run with s3 [1,0,-2,3,4,-7,0,-8] (0,0,0) producing
(3,3,2).
Just about any primitive recursive function can be translated into a fold.
Okay, I'm new to Haskell, and need help getting my head around functional approaches to problems.
I have a list of list. More specifically [[Char]].
I would like to target a specific element in a list, and look at the elements above, below, and to the left and right of the element.
For example:
["-------",
"-------",
"---N---",
"--WOE--",
"---S---",
"-------",
"-------"]
Where O is the element I'm targeting, and N,S,E,W are the elements directly surrounding the element O.
I'm made a function that gets an (x,y) coordinate of the element O, and it is as follows:
find :: Eq a => [[a]] -> a -> (Int,Int)
find [[]] _ = (-1,-1)
find (x:xs) el = findHelper (x:xs) (0,0,el)
findHelper :: Eq a => [[a]] -> (Int,Int,a) -> (Int,Int)
findHelper [[]] _ = (-1,-1)
findHelper (x:xs) (row,col,el)
| x == [] = findHelper xs (row+1,0,el)
| (head x) == el = (row,col)
| otherwise = findHelper ((tail x) : xs) (row,col + 1,el)
This (x,y) coordinate system is 0-indexed. I just can't for the life of me figure out once I have the position of the element I'm looking for, how to figure out what elements are surrounding it.
Sorry if this isn't the most clear of questions, and I can elaborate more if I missed something.
What you are trying to do is not always possible, since lists don't have fixed sizes. If you can ensure that however, it is possible to do this, otherwise you will need a lot of checks to prevent problems. I'll show a way you can get the surrounding elements, but you will have to build in some extra checks to make sure it does not try to lookup an element that is out of bounds for example.
This should get you a list with the surrounding elements:
findSurrounding :: [[a]] -> (Int, Int) -> [a]
findSurrounding matrix (x,y) = map (getElementAt matrix) positions
where positions = [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]
getElementAt :: [[a]] -> (Int, Int) -> a
getElementAt matrix (x, y) = (matrix !! y) !! x
I have not tested the code so it might need some tweaking, but it should get you started.
I thought I was smooth sailing in my Haskell studies, until...
I have a [[Int]]
tiles = [[1,0,0]
,[0,1,0]
,[0,1,0]
]
and a data type:
data Coord = Coord
{ x :: Int
, y :: Int
} deriving (Eq)
Based on the input tiles, I've been trying to output a [Coord], such that a Coord is only generated when the value of tiles is 1, and the Coord will store it's position in the 2d list:
blackBox :: [[Int]] -> [Coord]
blackBox tiles = <magic>
-- given the above example I would expect:
-- [(Coord 0 0),(Coord 1 1),(Coord 1 2)]
I have tried things like first converting [[Int]] to a [Int], via:
foldTiles :: [[Int]] -> [Int]
foldTiles tiles = foldr (++) [] tiles
but after that I'm not really sure how to pass the indices along. I suppose if I could map over the "folded tiles", outputting a tuple (value, index), I could easily figure out the rest.
update In case anyone's interested, I got it working and here is a demo of it (with source code and link to GitHub)! I will have to take more time to understand each of the answers as this is my first time programming a game using FP. Thanks a lot!
http://kennycason.com/posts/2013-10-10-haskell-sdl-gameboy-boxxle.html
This is a place where list comprehensions shine.
blackBox tiles =
[Coord x y -- generate a Coord pair
| (y, row) <- enumerate tiles -- for each row with its coordinate
, (x, tile) <- enumerate row -- for each tile in the row (with coordinate)
, tile == 1] -- if the tile is 1
Or you could go for the equivalent do notation (since list is a monad), which requires importing Control.Monad (for guard.)
blackBox tiles = do
(y, row) <- enumerate tiles -- for each row with its coordinate
(x, tile) <- enumerate row -- for each tile in the row (with coordinate)
guard (tile == 1) -- as long as the tile is 1
return (Coord x y) -- return a coord pair
To aid with understanding, this latter function works like the following Python function.
def black_box(tiles):
for y, row in enumerate(tiles):
for x, tile in enumerate(row):
if tile == 1:
yield Coord(x, y)
do notation for the list monad is incredibly handy for processing lists, I think, so it's worth wrapping your head around!
In both of these examples I have used the definition
enumerate = zip [0..]
Here's a simple solution (not guarantee that it's viable for tiles of size 10000x10000, that's something for you to check ;)
The approach is, as usual in Haskell, a top-down development. You think: what should blackBox do? For every row of tiles it should collect the Coords of the tiles with 1 for that row, and concatenate them.
This gives you another function, blackBoxRow, for rows only. What should it do? Remove zeros from the row, and wrap the rest in Coords, so there's filter and then map. Also you want to keep the row and column numbers, so you map tiles joined with their respective coordinates.
This gives you:
tiles :: [[Int]]
tiles = [[1,0,0]
,[0,1,0]
,[0,1,0]
]
data Coord = Coord {
x :: Int
,y :: Int
} deriving (Eq, Show)
blackBox :: [[Int]] -> [Coord]
blackBox tiles2d = concat (map blackBoxRow (zip [0..] tiles2d))
blackBoxRow :: (Int, [Int]) -> [Coord]
blackBoxRow (row, tiles1d) = map toCoord $ filter pickOnes (zip [0..] tiles1d) where
pickOnes (_, value) = value == 1
toCoord (col, _) = Coord {x=col, y=row}
main = print $ blackBox tiles
Results in:
~> runhaskell t.hs
[Coord {x = 0, y = 0},Coord {x = 1, y = 1},Coord {x = 1, y = 2}]
The way I see it, you could put your 2D list through a series of transformations. The first one we'll need is one that can replace the 1 in your list with something more useful, such as its row:
assignRow :: Int -> [Int] -> [Int]
assignRow n xs = map (\x -> if x == 1 then n else x) xs
We can now use zipWith and [1..] to perform the first step:
assignRows :: [[Int]] -> [[Int]]
assignRows matrix = zipWith assignRow [1..] matrix
What's handy about this is that it'll work even if the matrix isn't square, and it terminates as soon as the matrix does.
Next we need to assign the column number, and here I'll do a few steps at once. This makes the tuples of the coordinates, but there are invalid ones where r == 0 (this is why I used [1..], otherwise, you'll lose the first row), so we filter them out. Next, we uncurry Coord to make a function that takes a tuple instead, and then we use flip on it, then map this thing over the list of tuples.
assignCol :: [Int] -> [Coord]
assignCol xs = map (uncurry (flip Coord)) $ filter (\(c, r) -> r /= 0) $ zip [1..] xs
And we can build our assignCols:
assignCols :: [[Int]] -> [Coord]
assignCols matrix = concatMap assignCol matrix
which allows us to build the final function
assignCoords :: [[Int]] -> [Coord]
assignCoords = assignCols . assignRows
You could compress this quite a bit with some eta reduction, too.
If you want 0-indexed coordinates, I'll leave you to modify this solution to do so.
Quick and dirty solution:
import Data.Maybe (mapMaybe)
data Coord = Coord {
x :: Int
,y :: Int
} deriving (Eq, Show)
blackBox :: [[Int]] -> [Coord]
blackBox = concatMap (\(y, xks) -> mapMaybe (toMaybeCoord y) xks)
. zip [0..] . map (zip [0..])
where
toMaybeCoord :: Int -> (Int, Int) -> Maybe Coord
toMaybeCoord y (x, k) = if k == 1
then Just (Coord x y)
else Nothing
The zips pair the the tile values (which I am referring to as k) with the x and y coordinates (we are dealing with lists, so we have to add the indices if we need them). mapMaybe is convenient so that we can map (in order to construct the Coords) and filter (to remove the zero tiles) in a single step. concatMap also does two things here: it maps a function (the anonymous function within the parentheses) generating a list of lists and then flattens it. Be sure to check the types of the intermediate functions and results to get a clearer picture of the transformations.
Here it is, using list comprehensions.
blackBox :: [[Integer]] -> [Coord]
blackBox ts = [Coord x y | (t,y) <- zip ts [0..], (e,x) <- zip t [0..], e == 1]
As long as we're collecting answers, here's another:
blackBox :: [[Int]] -> [Coord]
blackBox ts = map (uncurry Coord) xsAndYs
where
xsAndYs = concat $ zipWith applyYs [0..] x1s
applyYs i = map (flip (,) i)
x1s = map (map fst . filter ((==1) . snd)) xs
xs = map (zip [0..]) ts
Explanation:
This assigns the x indexes within each row:
xs = map (zip [0..]) ts
Then I filter each row to keep only the elements with a 1, and then I drop the 1 (since it's no longer useful):
x1s = map (map fst . filter ((==1) . snd)) xs
Which results in something of type [[Int]], which are the rows with xs where 1s used to be. Then I map the ys within each row, flipping the pairs so I'm left with (x,y) instead of (y,x). As a final step, I flatten the rows into a single list, since I don't need to keep them separate anymore:
xsAndYs = concat $ zipWith applyYs [0..] x1s
applyYs i = map (flip (,) i)
Finally I convert each element by mapping Coord over it. uncurry is necessary because Coord doesn't take a tuple as argument.