Haskell访问列表理解中的元组数据

时间:2012-01-23 05:43:10

标签: haskell types tuples

我已按如下方式定义了自定义类型:

-- 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中的每个元组,但实际上我试图在已经递归的函数中使用这个嵌套,所以我宁愿不需要引入那种复杂性。

2 个答案:

答案 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