我已按如下方式定义了自定义类型:
-- Atom reference number, x coordinate, y coordinate, z coordinate, element symbol,
-- atom name, residue sequence number, amino acid abbreviation
type Atom = (Int, Double, Double, Double, Word8, ByteString, Int, ByteString)
我想收集具有一定残留序列号nm的所有原子。
这很不错:
[x | x <- p, d == nm]
where
(_, _, _, _, _, _, d, _) = x
其中p是原子列表。
但是,这不起作用,因为我不能在列表推导之外访问变量x,也不能想到从列表推导中访问特定元组值的方法。
我缺少一个元组方法,还是应该使用不同的数据结构?
我知道我可以编写一个递归函数来解包并检查列表p中的每个元组,但实际上我试图在已经递归的函数中使用这个嵌套,所以我宁愿不需要引入那种复杂性。
答案 0 :(得分:9)
这有效:
[x | (_, _, _, _, _, _, d, _) <- p, d == nm]
但是,您应该在这里定义您自己的数据类型。一个三元素的元组是可疑的;一个八元素的元组确实是个坏消息。与数据类型相比,元组难以使用且类型安全性较低(如果使用具有相同元素类型的两个元组表示两种不同类型的数据,则它们可以互换使用)。以下是我将Atom
写为记录的方式:
data Point3D = Point3D Double Double Double
data Atom = Atom
{ atomRef :: Int
, atomPos :: Point3D
, atomSymbol :: Word8
, atomName :: ByteString
, atomSeqNum :: Int
, atomAcidAbbrev :: ByteString
} deriving (Eq, Show)
(“atom”前缀是为了避免与其他记录中的字段名称发生冲突。)
然后您可以按如下方式编写列表推导:
[x | x <- p, atomSeqNum x == nm]
作为奖励,您对Atom
的定义将成为自我记录,您将获得增加类型安全性的好处。以下是使用此定义创建Atom
的方法:
myAtom = Atom
{ atomRef = ...
, atomPos = ...
, ... etc. ...
}
顺便说一句,将这些类型的某些字段设置为严格可能是个好主意,这可以通过在字段类型之前加上感叹号来完成;这有助于避免空间泄漏来自未评估的thunks积累。例如,因为在不评估其所有组件的情况下评估Point3D
没有多大意义,我会将Point3D
定义为:
data Point3D = Point3D !Double !Double !Double
将Atom
的所有字段都设置为严格可能是一个好主意,尽管可能不是全部;例如,ByteString
字段如果由程序生成,则应保持非严格,不总是访问且可能很大。另一方面,如果从文件中读取它们的值,那么它们应该是严格的。
答案 1 :(得分:4)
你应该肯定使用不同的结构。不要使用元组,请查看records。
data Atom = Atom { reference :: Int
, position :: (Double, Double, Double)
, symbol :: Word8
, name :: ByteString
, residue :: Int
, abbreviation :: ByteString
}
然后你可以这样做:
a = Atom ...
a {residue=10} -- this is now a with a residue of 10