Reputation: 607
Been having some real issues with this and haven't been able to find any guidance on doing this in any reading. Have been tasked with implementing functions to complete a Haskell version of Connect 4. The board is represented as a list of lists of pieces using Data.List.
One of the functions is to drop a piece given the piece and column number. For this I would like to just add the piece to the appropriate column and be done with it but the only way I seem to be able to do it is recursing through the list until I get to the right column and then add the piece.
Is there any way to do this better?
My horrendous code is below:
cheatPiece :: GameState -> Int -> Piece -> GameState
cheatPiece [] _ _ = []
cheatPiece (xs:xss) 0 x = (x:xs) : xss
cheatPiece (xs:xss) n x = xs : cheatPiece xss (n-1) x
Upvotes: 1
Views: 141
Reputation: 2818
You could use the take
and drop
functions and the list-indexing operator !!
.
cheatPiece xss n x = take n xss ++ [x : (xss !! i)] ++ drop (n + 1) xss
Or there's splitAt
which combines take and drop - I'll throw in a check for when the index is too big:
cheatPiece xss n x = case splitAt n xss of
(_, []) -> error "out of range"
(yss, zs:zss) -> yss ++ [x:zs] ++ zss
But I'd be tempted to generalize that by writing a function for modifying an element at an index:
modifyAt :: Int -> (a -> a) -> [a] -> [a]
modifyAt n f xs = case splitAt n xs of
(_, []) -> error "out of range"
(ys, z:zs) -> ys ++ [f z] ++ zs
which can be used like this:
> modifyAt 3 (+1000) [0..9]
[0,1,2,1003,4,5,6,7,8,9]
Then your function would be
cheatPiece xss n x = modifyAt n (x:) xss
Upvotes: 3
Reputation: 24802
I don't think your implementation is horrendous at all. That's pretty much the standard way to work with immutable, linked lists.
I think the main thing that makes it feel clumsy is that working with indices and linked lists is never going to be very natural.
So, in the context of a homework assignment, your implementation is, I think, the most correct way to implement cheatPiece
. If you had control over the board presentation I might consider using, for example, a vector
or an IntMap
to store the columns.
There's also always lens
which lets you work with nested, immutable structures using terser abstractions but if you are still new to Haskell then the lens
package definitely does not have the gentlest of learning curves.
import Control.Lens
data Piece = X | O deriving Show
type GameState = [[Piece]]
cheatPiece :: GameState -> Int -> Piece -> GameState
cheatPiece st i p = st & ix i %~ (p:)
Upvotes: 3