Haskell——在元组中操作数据

Haskell——在元组中操作数据,haskell,tuples,Haskell,Tuples,我正在尝试使用haskell模拟跳棋游戏。我得到了一个名为checkerstate的4元组,我想用几个不同的函数来处理它。到目前为止,我有一个函数oneMove,它从checkerState接收输入,并返回一个修改数据的元组: 输入元组: ( 3600, "", [ "----------", "------r---", "----------", "----------", "---r-r----", "-----

我正在尝试使用haskell模拟跳棋游戏。我得到了一个名为checkerstate的4元组,我想用几个不同的函数来处理它。到目前为止,我有一个函数oneMove,它从checkerState接收输入,并返回一个修改数据的元组:

输入元组:

    (
    3600,
    "",
    [
    "----------",
    "------r---",
    "----------",
    "----------",
    "---r-r----",
    "------r---",
    "---w---w-w",
    "----------",
    "----------",
    "------w---"
    ],
    (
    49
    ,
    43  
    )
    )
到目前为止,我有一些类似于下面定义的函数,但不确定如何访问tuple checkerState中的各个成员。此方法将花费一段时间、一组捕获的棋子、棋盘和移动来生成并返回一个时间、一组捕获的棋子和棋盘。目前,我想根据电路板的状态修改元组中的时间(INT):

    onemove :: (Int,[Char],[[Char]],(Int,Int)) -> (Int,[Char],[[Char]])

提前谢谢

您可以使用模式匹配来提取元素,执行任何需要进行的更改,并将它们打包回元组。例如,如果要增加第一个值,可以:

onemove (a,b,c,d) = (a + 1,b,c,d)
如果您发现自己经常这样做,您可能会重新考虑使用元组,而不是使用数据类型:

data CheckersState = CheckersState { time  :: Int       -- field names are just
                                   , steps :: [Char]    -- guesses; change them
                                   , board :: [[Char]]  -- to something that
                                   , pos   :: (Int, Int)  -- makes sense
                                   } deriving (Eq, Read, Show)
然后,您可以使用更方便的语法对其进行更新:

onemove state = state { time = time state + 1 }
如果您想坚持使用元组,并且碰巧正在使用,那么有另一种简单的方法来更新元组:

onemove = over _1 (+1)
或者,如果您使用的是镜头和您自己的数据类型(使用适当定义的访问器,如提供的访问器),您可以执行类似的操作:

_time :: Lens' CheckersState Int
_time f state = (\newTime -> state { time = newTime }) <$> f (time state)

onemove = over _time (+1)
\u time::Lens'CheckersState Int
_时间f状态=(\newTime->state{time=newTime})f(时间状态)
onemove=结束时间(+1)
所以有很多新奇的方法可以做到这一点。但最常用的方法是使用模式匹配。

您可以使用模式匹配将元组分解为变量

onemove (i, c, board, (x, y)) = <do something> (i, c, board)
onemove(i,c,board,(x,y))=(i,c,board)

但是,您应该为董事会定义一个单独的数据结构,以明确您的意图。我不知道前两个值是什么意思。请参阅:

正如icktoofay所说,使用元组是一种代码味道,使用命名组件的记录更好

此外,使用Char(和String)也是一种代码味道。要修复它,请定义一种数据类型,精确描述您在电路板单元中所期望的内容,如
data color=None | Red | Black
,但请参见下一项

而且,使用列表也是一种代码味道。实际上,您需要的是
type Board=Data.Map.Map Pos color
Data.Map.Map Pos(可能是color')
Data color'=红色|黑色

噢,Int也是一种代码味道。您可以定义
newtype Row=Row Int;新类型Col=Col Int;键入Pos=(行,列)
。可能是新类型的
派生Num
,但不清楚,例如,您不想将行号相乘。也许
派生(Eq、Ord、Enum)
就足够了,使用
Enum
可以得到
pred
succ


(啊-这个
Pos
使用的是一个元组,因此它很臭?嗯,不,有时允许使用两个元组。)

您是否考虑过编写一个
数据类型,也许是一个可以更容易地访问不同字段的类型?Haskell对包含两个以上元素的元组的支持有些有限(除非您使用像镜头这样的额外库),可能部分是为了阻止使用它们,正如其他人所说,创建数据类型通常会更清楚地显示您的类型的预期用途。不幸的是,数据结构和设计选择是由教授给出的。我可以将元组转换为数据类型,然后在操作完成后再转换回元组吗?这可能有些过分了我不支持当前的操作。我正在更新问题以添加更多细节,但您的变量实际上大部分是正确的。时间数据状态将根据线路板和pos移动之间的某种情况进行更新。@Dev:我想您可以在操作前后进行转换,但取决于操作的参与程度,时间数据状态的开销是多少与直接处理元组相比,转换可能会使它变得不那么清晰。