I'm new to Haskell and was trying to make a grid function which returns all the coordinates in the grid as a list. I want to filter this list to remove any coordinates where x = y given [(x,y)] (essentially getting rid of all the diagonal coordinates in the grid) but I'm not sure how to do it. Any help/pointers would be appreciated.
grid :: Int -> [(Int,Int)]
grid n = [(x,y) | x <- [0..n], y <- [0..n]]
Your function was good enough, just add a filter on it:
grid :: Int -> [(Int,Int)]
grid n = [(x,y) | x <- [0..n], y <- [0..n], x /= y]
using a filter (x /= y) in the list comprehensions is pretty simple and describes what you try to accomplish.
You should use filter.
The following predicate is True for non-diagonal elements:
predicate (x,y)=x/=y
Then you can define your filter:
gridFilter g=filter predicate g
Run it with:
gridFilter $ grid 2
Yields:
[(0,1),(0,2),(1,0),(1,2),(2,0),(2,1)]
Of course you can also make it a one-liner:
gridFiltered=filter (\(x,y)->x/=y) $ grid 2
Related
Im trying to make a function that takes a list of colours and a specific colour as input. It will look through the list of colours until it finds the specific colour and put it in the front. I want to do this all while perserving the order of the rest of the numbers.
So for example:
putfront [] (red:blue:green:yellow:white:pink:orange:[]) yellow
returns
yellow:red:blue:green:white:pink:orange:[]
This is my attempt:
putfront::[Colour]->[Colour]->Colour-> [Colour]
putfront leftofcurrent (current:rightofcurrent) thecolour
|current==thecolour = thecolour:leftofcurrent:rightofcurrent
|otherwise= putfront (leftofcurrent:current:[]) rightofcurrent thecolour
But Haskell doesn't seem to be too happy with:
thecolour:leftofcurrent:rightofcurrent
and
(leftofcurrent:current:[])
saying:
Expected type: Pond_Ix
Actual type: [Pond_Ix]
What is wrong?
But Haskell doesn't seem to be too happy with:
thecolour:leftofcurrent:rightofcurrent
That's because leftofcurrent is a List. If you want to combine those lists, it needs to be:
(thecolour:leftofcurrent) ++ rightofcurrent
Likewise, (leftofcurrent:current:[]) is impossible because leftofcurrent is a List, but this is probably what you're after:
leftofcurrent ++ [current]
The cons operator : expects a single element on the left and a list on the right. But because of its fixity, you can chain them together so that a bunch of single items are separated by : and ended with a list. That is,
item1:item2:item3:[]
-- is the same as
item1:(item2:(item3:[]))
Also, your pattern matching is non-exhaustive. You need to handle the case where the second list parameter is empty.
putfront leftofcurrent [] thecolour = ...
I would write it as follows
putfront :: (Eq a) => a -> [a] -> [a]
putfront a x | a `elem` x = a: filter (not . (==a)) x
| otherwise = x
> putfront 4 [1..8]
[4,1,2,3,5,6,7,8]
you can recursively write it as well
putfront a x = bubble [] x
where bubble p [] = p
bubble p (s:ss) | a==s = a:p++ss
| otherwise = bubble (p++[s]) ss
> putfront 3 [1..5]
[3,1,2,4,5]
> putfront 7 [1..5]
[1,2,3,4,5]
I actually think that your best soln is to remove the item and put it at the front of the list, as you mention in the comment. Given that you don't like this, may I suggest the following
sortBy (\x y -> case (x, y) of (_, theColour) -> GT; (x, y) ->LT) listOfColours
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.
Why is there a parse error on this? I insert a list and want to get tuples out. (The top line is correct).
freq :: Eq a => [a] -> [(Int,a)]
freq x:xs = [(x,y)| (x,y) x <- count , y <- rmdups]
There are two syntax errors here — no parenthesis on the pattern, and wrongly placed (x,y) inside the comprehension. It should be:
freq (x : xs) = [(x, y) | x <- count, y <- rmdups]
You have to put parenthesis in your pattern match
freq (x:xs) = {- ... -}
I have a graph like this:
As part of a homework assignment I want to find the triangle (1->2->5). I have no idea how to find this.
In my case, I defined my graph:
type Graph = (Int, Int -> Int -> Bool)
g 2 3 = True
g 3 2 = True
g 1 2 = True
g 2 1 = True
g 1 1 = True
g n m = False
Answer to 2 comment.
I did this and it works, I think.
triangles :: [(Int, Int, Int)]
triangles = [(x, y, z) | x <- [1..3], y <- [1..x], z <- [1..y], isTriangle (x, y, z)]
isTriangle :: (Int, Int, Int) -> Bool
isTriangle (x, y, z) = g x y && g y z && g x z
I removed (_,g) and (n,g) (I dont understand why we need them :)
I call trinagles and it return (1,1,1) (2,1,1) (in my case). Is it right?
I guess the first Int of Graph is a bound for your nodes (like, 6 if the nodes are in [1..6]).
Therefore, you would like a function that returns the triangles of a graph, so the type might be:
triangles :: Graph -> [(Int, Int, Int)]
Now, a triangle exists whenever, for 3 nodes, say x y and z, all the combinations return True through g.
So, you might want to consider generating all these combinations (possibly avoiding the ones that are equivalent via re-ordering), and filter out only those that validate the criterion:
isTriangle :: Graph -> (Int, Int, Int) -> Bool
isTriangle (_, g) (x, y, z) == g x y && g y z && g x z
For this, you could use a list comprehension, or the function filter which has type (a -> Bool) -> [a] -> [a]
Answer to your first comment:
First, you would need to implement the triangles function, which is the reason of the error. But, as you have done in test, you could simply generate these triangles on the fly.
Now, you wrote:
test = filter (isTriangle) [(x,y,z) | x <- [1..3], y <- [1..3], z <- [1..3]]
Two things about this:
First, you wouldn't need the parentheses around isTriangle for what you wrote, but it is incorrect, since isTriangle expects a graph as its first parameter
Second, you are going to obtain a lot of duplicates, and if you want, you can prevent this by not generating them in the first place:
test = filter (isTriangle) [(x,y,z) | x <- [1..3], y <- [1..x], z <- [1..y]]
Alternatively, you can dismiss the filter function by providing a guard in the list comprehension syntax, as this:
[(x, y, z) | x <- [1..3], y <- [1..x], z <- [1..y], isTriangle yourGraph (x, y, z)]
Now, I'll let you go on with the details. You will want to make this a function that takes a graph, and to replace this 3 by the number of nodes in the graph, and yourGraph by said graph.
Since you chose to use list comprehension, forget about the generating function that I wrote about earlier, its purpose was just to generate input for filter, but with the list comprehension approach you won't necessarily need it.
Answer to your second comment:
You want to write a function:
triangles :: Graph -> [(Int, Int, Int)]
triangles (n, g) = [(x, y, z) | ...]
The ... are to be replaced with the correct things, from earlier (ranges for x, y and z, as well as the predicate isTriangle).
Alternatively, you can cut this in two functions:
allTriangles :: Int -> [(Int, Int, Int)]
allTriangles n = [(x, y, z) | ...]
graphTriangles :: Graph -> [(Int, Int, Int)]
graphTriangles (n, g) = [t | t <- allTriangles n, isGraphTriangle t]
where isGraphTriangle (x, y, z) = ...
This way, you could potentially reuse allTriangles for something else. If you don't feel the need, you can stay with the one-shot big comprehension triangles, since it's a homework you probably won't build up on it.
I try not to fill all the ... so that you can do it yourself and hopefully understand :)
Correcting your solution:
First, my mistake on the ranges, it should be x <- [1..n], y <- [x+1..n], z <- [y+1..n] where n denotes the number of nodes in your graph. This way, you only capture triples where x < y < z, which ensures that you only see one occurence of each set of three points.
Second, the reason why I put the graph as a parameter to the functions is that you might want to reuse the same function for another graph. By hardcoding g and 6 in your functions, you make them really specific to the particular graph you described, but if you want to compute triangles on a certain number of graphs, you do not want to write one function per graph!
I don’t really like your graph type but whatever. Here’s the algorithm we will use:
First find a Node x of the graph.
For every other node y see if it connects to x
If y does connect to x then for each node z, see if it connects to x and y
If so then return it.
To avoid duplicates, we require z<x<y
nodes (n,_) = [1..n]
nodesBefore (n,_) k = [1..min n (k - 1)]
edge (_,e) x y = e x y
neighboursBefore g x = [ y | y <- nodesBefore g x, edge g x y]
triangles g = [(x,y,z) | x <- nodes g, y <- neighboursBefore g x, z <- neighboursBefore g y, edge g x z]