简体   繁体   中英

Altering A Single Value in List of Lists in Haskell [Homework]

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

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:)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM