List 迭代所有(行、列)可能性?
这将是一个TicTacToe实施:List 迭代所有(行、列)可能性?,list,loops,haskell,List,Loops,Haskell,这将是一个TicTacToe实施: data Row = A | B | C deriving (Show, Read, Eq, Ord, Enum, Bounded) data Column = X | Y | Z deriving (Show, Read, Eq, Ord, Enum, Bounded) type Pos = (Row, Column) data Player = Player String data Field = Field [(Pos, Player)] in
data Row = A | B | C deriving (Show, Read, Eq, Ord, Enum, Bounded)
data Column = X | Y | Z deriving (Show, Read, Eq, Ord, Enum, Bounded)
type Pos = (Row, Column)
data Player = Player String
data Field = Field [(Pos, Player)]
initialField :: Field
initialField = Field []
如您所见,initialField只是一个空列表,当玩家移动时,(pos,player)元组将添加到列表中。到现在为止,一直都还不错。但是现在我很难编写possibleMoves::Field->[Pos]
函数来获取空字段。如何迭代Haskell中的所有行、列可能性?我觉得我的方法是错误的,但我对Haskell是新手,所以我没有更好的想法。所有行(ref=):
所有列:
Prelude> [(minBound :: Column) ..]
[X,Y,Z]
计算笛卡尔积以找到所有可能性(ref=):
Prelude>[(x,y)| x我们可以通过列表理解获得所有位置(另请参见其他答案)
然后我们可以定义可能的移动(您将需要导入Data.List以获取\\
,列表差异):
更紧凑的版本利用了列表理解约束:
possibleMoves :: Field -> [Pos]
possibleMoves (Field l) = [(r,c) | r <- [A,B,C], c <- [X,Y,Z], (r,c) `notElem` occupied]
where occupied = fmap fst l
possibleMoves::Field->[Pos]
可能移动(字段l)=[(r,c)| r如果您想要超短的“ulta Haskelly”版本:
这到底是怎么回事
enumAll
只是一个通用的助手函数(我很惊讶不能在标准库中快速找到它;可能我错过了它,您甚至不需要自己定义它)。它为您提供了任何有界可枚举类型的所有可能性的列表。它相当简单;bounded
表示该类型具有minBound
和maxBound
,Enum
表示您可以使用[a..b]
语法获取从a
到b
的所有内容的列表
(,)
只是结对功能。如果在ghci中键入:t(,)
,它会告诉您(,)::a->b->(a,b)
现在,那些奇怪的
和
符号呢?它们基本上是函数应用程序的“特殊”形式。因此,你可以阅读它,就像我们只是将(,)
应用于两个参数enumAll
和enumAll
一样。但因为它是“特殊”应用程序,它不只是给我们一对(enumAll,enumAll)
我们在这里所做的是使用Applicative
实例进行列表。我不打算详细介绍,但这有助于我们认为[Row]
不是一个行值列表,而是一个“未知”列表行值。它可以是列表中的任何元素,但我们不知道是哪个元素。通常用于此的技术术语是[row]
可以被认为是一个不确定的行
;它是一个行
值,可能有许多可能性,但我们无法确定它实际上是哪一个
因此,我们正在做的是将函数(,)
(只需要两个参数来构建一对)应用于两个非确定性值,以获得一个非确定性对(这就是我们需要使用
和
的函数应用程序的“特殊”版本;如果我们通常使用(,)
(,)enumAll enumAll
或直接使用(enumAll,enumAll)
构建该对,然后我们得到的是一对不确定的正常值,而不是一对不确定的正常值-([a],[b])
vs[(a,b)]
).如果我从一个有界枚举的所有可能性和另一个有界枚举的所有可能性中取一对,那么我应该得到pair的所有可能性!这正是所发生的
类型签名positions::[Pos]
在这里实际上是必要的。这就是告诉Haskell我们正在建立一个列表(行,列)
对而不是任何其他类型的枚举对,这就是它如何知道第一个enumAll
枚举所有行,第二个wsa枚举所有列。最通用的类型(,)enumAll enumAll
实际上是(Enum a,Bounded a,Enum b,Bounded b)=>[(a,b)]
,这将很好地工作,但在ghci中以交互方式工作是一件痛苦的事情,因为每当您尝试打印内容时,您都会得到不明确的类型变量。谢谢,最后一个解决方案非常好:)。甚至有`notElem`
可以用来节省一对括号。
Prelude> [(x, y) | x <- [(minBound :: Row)..], y <- [(minBound :: Column)..]]
[(A,X),(A,Y),(A,Z),(B,X),(B,Y),(B,Z),(C,X),(C,Y),(C,Z)]
positions :: [Pos]
positions = [(r,c) | r <- [A,B,C], c <- [X,Y,Z]]
occupied :: Field -> [Pos]
occupied (Field l) = fmap fst l
possibleMoves :: Field -> [Pos]
possibleMoves f = positions \\ (occupied f)
possibleMoves :: Field -> [Pos]
possibleMoves (Field l) = [(r,c) | r <- [A,B,C], c <- [X,Y,Z], (r,c) `notElem` occupied]
where occupied = fmap fst l
enumAll :: (Bounded a, Enum a) => [a]
enumAll = [minBound..maxBound]
positions :: [Pos]
positions = (,) <$> enumAll <*> enumAll
*Main> positions
[(A,X),(A,Y),(A,Z),(B,X),(B,Y),(B,Z),(C,X),(C,Y),(C,Z)]