我目前正在学习Haskell,目前我遇到了一个问题。
这是我的代码:
data Pos = Pos (Int, Int) deriving (Show, Eq)
allCoordinates :: [Pos]
allCoordinates = concat [(zip (allHelper x) [0..8]) | x<-[0..8]]
allHelper ::Int -> [Int]
allHelper x = [x | y<-[0..8]]
我必须使用该特定数据类型,因此我遇到了一些问题。
如果代码编写如下,则此代码可以正常工作:
type Pos = (Int, Int)
allCoordinates :: [(Int, Int)]
allCoordinates = concat [(zip (allHelper x) [0..8]) | x<-[0..8]]
但我需要编辑这个位:
concat [(zip (allHelper x) [0..8]) | x<-[0..8]]
以便它适用于:
allCoordinates :: [Pos]
这可能是一个简单的问题而且我有一点精神障碍但是有人可以帮忙吗?
谢谢
答案 0 :(得分:3)
如果你写:
type Pos = (Int, Int)
您不构建了一个新类型:您构建了一个类型别名,因此您可以在代码中将Pos
与(Int, Int)
互换。因此,带有签名f :: [(Int,Int)]
的函数等同于f :: [Pos]
。
您可以清理代码并写下:
allCoordinates :: [Pos]
allCoordinates = [ (x,y) | x <- [0..8], y <- [0..8]]
甚至更干净(但可能有点难以理解):
allCoordinates :: [Pos]
allCoordinates = (,) <$> [0..8] <*> [0..8]
使用类型签名这一事实的一个缺点是,在该类型上可能定义了许多函数,并且您可能希望使用同名定义函数(以防万一)类型类),但具有不同的实现。在这种情况下,您可以定义数据类型:
data Pos = Pos (Int,Int)
现在我们已经使用构造函数Pos
定义了一个 new 类型。现在上面的代码将不再起作用,但我们可以通过调用我们生成的元组的构造函数来轻松地改变它。所以:
allCoordinates :: [Pos]
allCoordinates = [ Pos (x,y) | x <- [0..8], y <- [0..8]]
如果您使用一个构造函数定义类型,并且该构造函数具有一个参数,我们可以使用newtype
。现在我们已经定义了一个类型,但是Haskell可以优化它,这样在内部,它不会在构造函数中包装和解包,而只是以另一种方式处理元组:
newtype Pos = Pos (Int,Int)
答案 1 :(得分:1)
type
定义类型别名。也就是说,写作:
type Pos = (Int, Int)
表示无论何时在代码中编写Pos
,编译器都会看到(Int, Int)
。要在这种情况下构建Pos
,您只需撰写(1, 2)
,因为(Int, Int)
和Pos
无法区分。
但是,data
定义了一种新的数据类型。你的例子是:
data Pos = Pos (Int, Int)
Pos
是由函数Pos :: (Int, Int) -> Pos
构造的类型。 这些不相等!要在这种情况下构建Pos
,您需要编写Pos (1, 2)
。
要更正您的代码,请写下:
data Pos = Pos (Int, Int)
allCoordinates :: [(Int, Int)]
allCoordinates = concat [zipWith (curry Pos) (allHelper x) [0..8] | x<-[0..8]]
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
。
但是,除非您想在类型类中使用不同的行为,否则使用类型别名最好,否则,您应该使用newtype。