Basic Haskell functions
I am new to Haskell and I am trying to learn the basics. I need to declare the Pos type for the position, which will be two integers, and then I need to have North, South, West, East directions so that I can change the position based on the direction. Once I do that, I need to make a function called "moves" that will take the list of moves and the starting position and return the position after all the moves. Here is my code, but I am stuck at the point that I need to iterate over the list of moves.
type Pos = (Int, Int)
data Direction = North | South | East | West
move :: Direction -> Pos -> Pos
move North (x,y) = (x, y+1)
move West (x,y) = (x-1, y)
move South (x,y) = (x, y-1)
move East (x,y) = (x+1, y)
moves :: [Direction] -> Pos -> Pos
moves [] (x,y) = (x,y)
moves (h:xs) (x,y)
| h == North = move North (x,y)
| h == West = move West (x,y)
| h == South = move South (x,y)
| otherwise = move East (x,y)
What am I missing here?
source to share
Either before or after the only move that matches h
, you need to make a recursive call moves
for the rest of Direction
s. If before, each case becomes something like:
move North (moves xs (x,y))
if you want it after, which is probably the correct approach given the specification of the problem, then it would be:
moves xs (move North (x,y))
However, repeating the parsing of the case in moves
is a bit unnecessary, since you can simply call move
directly with h
, eg.
moves (h:xs) (x,y) = moves xs (move h (x,y))
In fact, you don't even need to explicitly match patterns in the tuple (x,y)
;
moves (h:xs) pos = moves xs (move h pos)
As another answer points out, this is the same as a built-in function foldl
(down to the order of the arguments), but it is probably best to understand how to do it manually.
source to share
Combining a list of things into one thing is called collapsing the list. There are (approximately) two folds of the list: foldl
and foldr
. Relying on the use of folds is an important step in learning Haskell. We need foldl
one that has the type
foldl :: (a -> b -> a) -> a -> [b] -> a
Now foldl
works with a merge function and an initial value to merge stuff in a list like
foldl (£) s [x,y,z] = (((s £ x) £ y) £ z)
( l
in foldl
is short for left, so it helps you remember that your starting value s
will end on the left, but more importantly, the parentheses are linked to the left).
The third argument is the list argument [b]
. We will use this for the list of moves, so the type b
will be Direction
.
The second argument is the start value of the type a
, so we'll use that for your start position, so the type a
will be Pos
.
The first argument is a function to combine something from your list with the current value. Now that we know that the types b
and a
are Direction
and Pos
, we know that our union function must have a type Pos -> Direction -> Pos
. The move function is pretty much what we want, except that we need to change the arguments. The function flip
does this, so it flip move
has the type we need.
So we will specialize the type foldl
as
foldl :: (Pos -> Direction -> Pos) -> Pos -> [Direction] -> Pos
and define
moves :: [Direction] -> Pos -> Pos
moves ds i = foldl (flip move) i ds
Now foldl
has a "strict" version called it foldl'
, which is faster in this case, so if you've been using it in a fast-paced game or doing very many moves, you would want to use this one.
As always, you can find functions by searching for their name or type on hoogle .
There is also a foldr function that folds the list in a different way. You can read about the difference between the two in this question . In short, it foldr
works like this:
foldr (?) s [x,y,z] = (x ? (y ? (z ? s)))
( r
In foldr
a short to the right, so it will help you remember what the initial value s
will be right, but, more importantly, that the brackets are connected to the right.)
source to share