Converting from one representation of a "Shape" to another - haskell

The following definitions represent a shape composed of coloured squares at specific coordinates on a grid:
type AltShape = [Point]
data Point = P Colour (Int,Int) deriving Eq
data Colour = Black | Red | Green | Blue deriving Eq
I should asume that coordinates always are positive and the coordinate (0,0) refers to the top left square of the picture, and the y-coordinates grow downwards
A Red L-shape could be represented by
lShape = [P Red (0,0), P Red (0,1), P Red (0,2), P Red (1,2)]
A different way to represent such shapes is as a list-of-lists, one list for each row:
type Shape = [Row]
type Row = [Square]
type Square = Maybe Colour
For example, the red L-shape above would be represented by the following value of type Shape:
lShape2 = [[x,o]
,[x,o]
,[x,x]] where x = Just Red
o = Nothing
My task is to define a function toShape :: AltShape -> Shape that converts from a AltShape to an Shape. I had another task to define a function fromShape :: Shape -> AltShape but where data Shape = S [Row]. I found that rather simple and wrote it like this:
fromShape :: Shape -> AltShape
fromShape (S rows) = [ P c (x,y) | (y,row) <- index rows, (x,Just c) <- index row]
where index = zip [0..]
However, I am having more trouble with this one. I started by creating the function
colourAndCoords :: Point -> (Colour,(Int,Int))
colourAndCoords ( P c (x,y) ) = (c,(x,y))
I then created a function
coords :: [Point] -> [(Int,Int)]
coords ps = map snd (map colourAndCoords ps)
My thought was to compare this list to another list of all possible coordinations and where there was a match add the right colour, and where there wasn't I would add Nothing. However, my teacher said I was making it too complicated and that I should think of another solution. But I am having a hard time thinking of one. So I guess my question is what is the easier way? I am not asking for a solution, just a nudge in the right direction.
Thanks a ton for anyone taking the time to read this and respond!!
If I come up with a solution I will come back and update this thread.

If you want an efficient solution, the accumArray function does almost exactly what you need (after computing the appropriate bounds).
λ> arr = accumArray (const Just) Nothing ((0, 0), (2, 1)) [((y, x), c) | P c (x, y) <- lShape]
λ> arr
array ((0,0),(2,1)) [((0,0),Just Red),((0,1),Nothing),((1,0),Just Red),((1,1),Nothing),((2,0),Just Red),((2,1),Just Red)]
λ> elems arr
[Just Red,Nothing,Just Red,Nothing,Just Red,Just Red]
Now the problem is reduced to split elements into groups.
λ> chunksOf 2 (elems arr)
[[Just Red,Nothing],[Just Red,Nothing],[Just Red,Just Red]]
For a real application you’d probably want to leave it as an array, since array indexing is fast (O(1)) and list indexing is slow (O(n)).

If efficiency is no concern, you could consider this approach:
Figure out the largest x coordinate (call it w), and the largest y coordinate (call it h).
Create a rectangular list of lists, length w by h, with a list comprehension or similar. At each position, look through the entire list of points for one with a matching position, and use its color if you find one (Nothing otherwise).
If efficiency is a concern, you could consider a more complicated approach:
Turn your [AltShape] into an IntMap (IntMap Colour), e.g. using this technique, which maps y and x to colour.
Iterate over occupied rows with toAscList; within each, iterate over occupied columns with toAscList. You will need to manually pad unoccupied rows with [] and unoccupied columns with Nothing.
An advantage (or possibly disadvantage, depending on your goals!) of the second approach is that it will naturally produce "ragged" Shapes which omit trailing Nothings.

Related

How can I generate every possible solution of a varied size grid which has n points

To give overall context, I have a 2d grid of size N (the size is known but can vary and is always a square i.e x axis = y axis) and M points on the grid that I'm trying to find (the amount of points is known but their locations are not). I want to iterate through every possible solution for said grid. Also the points don't have to be in a specific order, so
[(1,1),(1,2)]
is the same as
[(1,2),(1,1)]
So for example assume the grid was a 2x2 and their was 2 points. Then all the possible solutions would be
[(1,1),(1,2)]
[(1,1),(2,1)]
[(1,1),(2,2)]
[(1,2),(2,1)]
...and so on...
I'm trying to create a function that will output these to me.
I know the function below can create me a full grid, but I don't know how to use this to generate all the possible point locaitons. And whether this grid is even useful in the first place
createGrid :: Int -> [(Int, Int)]
createGrid num = [ (x,y) | x <- [1..num], y <- [1..num]]
Any help would be appreciated
It sounds like you just want a list of the unique ways to choose M items from a list (grid) of N*M items. You already know how to generate this list, so all you need is the ways to choose K items from a list. This is a well-trodden path; for example, see Function to generate the unique combinations of a list in Haskell.
In general it is useful to try to do this sort of splitting up: break your program into smaller pieces and see how many of them are easy to solve. If you try to do everything all at once in one function, you end up repeating work and often wind up with a function that is difficult to read.
For a given grid, if m is 0, then we return the empty list, when m is greater than 0, then we yield a point and recurse on the rest of the list, so:
possibleGrids :: Int -> Int -> [[(Int, Int)]]
possibleGrids n mm = go mm 1 0
where go 0 _ _ = [[]]
go m i j = [(x, y) : tl | x <- [i .. n], y <- [1 .. n], x > i || y > j, tl <- go (m-1) x y]
The first parameter here is n, the size of the grid. The second one is m, the number of points to mark. For a 2×2 grid, this gives us:
ghci> possibleGrids 2 0
[[]]
ghci> possibleGrids 2 1
[[(1,1)],[(1,2)],[(2,1)],[(2,2)]]
ghci> possibleGrids 2 2
[[(1,1),(1,2)],[(1,1),(2,1)],[(1,1),(2,2)],[(1,2),(2,1)],[(1,2),(2,2)],[(2,1),(2,2)]]
The code uses symmetry breaking, so it will not mark twice (or more) the same point, nor will it provide the list of points in a different order.

How to draw lines in Haskell?

I have to write a function drawLines that creates a view of defined lines. The first parameter is a tuple (columns, rows) defining the size of the resulting view. Left top corner has a coordinate (0,0). The second argument is a list of lines.
There is function for print:
pt :: Result -> IO ()
pt x = putStr (concat (map (++"\n") x))
Function:
drawLines :: (Int,Int) -> [Line] -> Result
Data types representing a point and a line:
data Point = Point Int Int
data Line = Line Point Point
There is IO example:
Prelude>pt(drawLines (31,15) [Line (Point x y) (Point 15 7)|(x,y)<-concat [[(x,y)|y<-[0,7,14]]|x<-[0,15,30]]])
##.............#.............##
..##...........#...........##..
....##.........#.........##....
.....###.......#.......###.....
........##.....#.....##........
..........###..#..###..........
.............#####.............
###############################
.............#####.............
..........###..#..###..........
........##.....#.....##........
.....###.......#.......###.....
....##.........#.........##....
..##...........#...........##..
##.............#.............##
Please help me. I tried something, but it doesn't work. Can someone explain to me how it works.
I tried this:
type Result = [String]
pt :: Result -> IO ()
pt x = putStr (concat (map (++"\n") x))
drawLines :: (Int,Int) -> [(Int,Int)] -> Result
drawLines (0,0) (x:xs) = [] drawLines (a,b) [] = []
drawLines (a,b) (x:xs) =[ [if a == fst x && b == snd x then 'x' else '.' | b
<- [1..b]]| a <- [1..a]] ++ drawLines (a,b) xs
I'm a beginner in Haskell language.
Any advice will be good for me.
Thank you.
This is called "rasterization". There is a simple trick by which libraries such as diagrams rasterize their vector images, and I will let you in on it. Then you will have easy time writing the code.
The trick is this:
Define a function that determines if a point with given coordinates should be painted black or white.
Map this function all over the grid.
For example, suppose you have a line Line (Point 1 2) (Point 3 4). You must be able to answer the question: should Point 2 3 be black? Does the given line pass close enough to the given point for it to be painted black?
shouldIPaint :: Point -> Line -> Colour
Now, if you have 2 lines, and either one is close enough, you paint, and so on for any number of lines.
overlay :: [Colour] -> Colour
shouldIPaintMany :: Point -> [Line] -> Colour
shouldIPaintMany point = overlay . fmap (shouldIPaint point)
Seeing how a view is a collection of points, you need to determine the value of this function for each point. To this end, you may first create a view where every point holds its coordinates as values, then convert the coordinates to colour.
initialView :: Point -> View Point
solution :: Point -> [Line] -> View Colour
solution point lines = fmap (flip shouldIPaintMany lines) (initialView point)
Done! And the beauty here is that it is straightforward to extend this solution to paint in 3 dimensional space, or to paint circles as well as lines, and so on.
I do not really expect you to be able to fill in the missing parts all by yourself. You may have any number of questions by now, but these will be specific, sharp questions. Some of them will be about Mathematics, others will be about Haskell. If you want to be successful, you should write down these sharp questions, then try to answer them, and if you cannot, then ask them here on Stack Overflow or, maybe, at Mathematics Stack Exchange. You can start by writing me a comment below this answer, and I will tell you if you are on the right track. It is also a possibility that you will not understand the solution I offered at first, but you really need to persist about it, and ask me if something does not make sense despite your best effort.

Is this Haskell function recalculating needlessly?

I want to compute values in an infinite grid of integers, where each square contains an integer from 0 to 3. I designed a function that increments a square: it adds one to the value of that square, but if the square would then hold a 4, it sets the square to 0 and calls itself on the four adjacent squares.
type Board = (Int,Int) -> Int
setSquareTo :: Int -> (Int,Int) -> Board -> Board
setSquareTo n (x,y) b (z,w) | x == z && y == w = n
| otherwise = b (z,w)
incSquare :: (Int,Int) -> Board -> Board
incSquare (x,y) b | b (x,y) == 3 = incSquare (x+1,y).incSquare (x,y+1).incSquare (x-1,y).incSquare (x,y-1).setSquareTo 0 (x,y) $ b
| otherwise = setSquareTo (b (x,y) + 1) (x,y) b
Now, I think this is doing only the calculations it needs to when finding the value of one square in a board generated by incSquare. However, it seems like finding the values of two adjacent squares one after the other wastes a lot of time doing the some of the same calculations in each case. Is this true?
If so is there a way a simple way of improving performance? Would something like
type Board = IntMap (IntMap Int)
using the strict version of IntMap be better? (How would I set every square to 0 initially in that case?)
This is the kind of thing I wrote data-inttrie for. It behaves just like a function from Int, except that it supports efficient single-point modifications through memoization.
You can initialize with any function f with fmap f identity, then use overwrite and modify to change it one value at a time.
Using a function as Board type is not such a good idea since the functions get more and more complicated when more calls to setSquareTo are made.
Then more and more space will be used to save this more and more complicated function. Also, when calling it with a value (x, y) for which setSquareTo hasn't been called for a long time, it takes a long time because the function first checks if the supplied parameter is one of the other coordinates.
So you should use Map (Int, Int) Int or your proposed IntMap (IntMap Int) instead as type for Board instead.
If you use Map (Int, Int) Int, you can use findWithDefault (x, y) 0 b instead of b (x, y) in this case to get 0 if this value doesn't yet exist in the map, so you don't have to initially set every value to 0 which isn't possible when you have an infinite board.

Haskell Store Data As Algorithm progresses

Say I had a Haskell algorithm which, through recursion, progressed through a Cartesian plane where each x/y coordinate had a specific value.
Position (0,0) is known, each other can be calculated by progressing back to the origin.
For example, to look at (0,3) I need to look at (-1,2), (0,2) and (1,2), which have to look in turn at (-2,1),(-1,1),(0,1) and (-1,1),(0,1),(1,1) and (0,1),(1,1),(2,1) respectively.
In order to avoid for (-1,1) and (0,1) to be calculated twice, is there any way I can create a data structure so that the algorithm can first look to see if a certain position ahs been calculated already and, if not, only then proceeds to calculating it?
Thanks :)
It sounds like memoization as m09 and cdk suggest might be what you're looking for. But if you want an algorithm that returns an array of positions, then simple boxed arrays (meaning they hold lazy values) and some knot tying can give you a nice declarative solution (sorry for the ugly code)
import Data.Array
-- for positive u
plane :: Int -> Array (Int,Int) Int
plane u = let knownOrigin = ((0,0) , 0)
l = negate u
otherCoords = [ (x,y) | let cs = [l .. u]
, x <- cs , y <- cs
, (x,y) /= (0,0)
]
a = array ((l,l),(u,u)) $
knownOrigin : map solution otherCoords
-- example recursive thing, referenceing lazy values in 'a':
solution c#(x,y) = let x' | x <0 = x+1
| x >0 = x-1
| x==0 = 0
y' | y <0 = y+1
| y >0 = y-1
| y==0 = 0
in (c , a ! (x',y') + 1)
in a
You could also use a Map or vector, or any other lazy structure that works best for your problem.

How to recurse through a list of tuples?

I am attempting to make a very simple program that finds the average distance a group of points is from 0. The input will be a group of tuples (i.e. (1,2) and (2,3)), it should calculate each points distance from 0 and then find the average.
I understand the logic and the general formula (again, it's very simple), which I have here:
averageDist ((x,y):xs) = (sqrt((x*x)+(y*y)))/length xs
I just don't understand the syntax for lists when using tuples. As you can see, I tried (x,y):xs and while it compiles, ghci thinks that all values are different types. How can I fix this program so I can iterate and apply the formula to each tuple?
I'm totally new to Haskell and I appreciate any and all help.
Thanks
First write an average function:
average :: [Double] -> Double
average xs = ...
and a distance function:
distance :: (Double,Double) -> Double
distance (x,y) = ...
Then you can use these functions together with map:
averageDistance :: [(Double,Double)] -> Double
averageDistance xs = average ( map distance xs )

Resources