将bibtex条目转换为另一种数据类型

时间:2012-02-29 19:00:16

标签: haskell

我想将bibtex entry转换为我自己的自定义数据类型,类似于:

date MyEntry = MyEntry {
    ident :: String,
    author :: [String],
    address :: String
}

authoraddress等字段存储在bibtex的字段数组中:

data T =
Cons {
   entryType :: String,
   identifier :: String,
   fields :: [(String, String)]
}
deriving (Show)

以上的例子如下:

  

缺点{entryType =" Book&#34 ;, identifier =" Arrighi",fields = [(" author",   " Arrighi,Gino"),(" title"," Leonardo Fibonacci:La Pratica di   Geometria。 (Volgar izzata da Cristofano di Gherardo di Dino,cittadino   皮萨诺。 Dal Codice 2186 dell a Biblioteca Riccardiana di Firenze。)/ A.   cura e con introductionzione di Gino Arrigh   I"),("地址""比萨&#34),("出版者""多莫斯   Galilaeana"),(" date"," 1966"),(" note","(Testimonianze di storia della)   scienza,3)"),("语言","意大利语"),(" pagetota   升"" 234&#34),("连字符""意大利&#34)]}

如何在其上进行模式匹配以将其转换为我的数据类型?

我立刻被卡住了(这不起作用):

toEntry  Cons { @entryType, @ident, @fields } = toEntry' entryType' ident fields'

toEntry' entryType ident fs = MyEntry { ident, entryType, ???????? }

2 个答案:

答案 0 :(得分:4)

您可以按照@ehird的描述对记录进行模式匹配,但我想您还询问了如何将“字段”列表转换为“作者”和“地址”字段。

您可以使用标准Prelude功能“lookup”。它的工作原理如下:

lookup :: k -> [(k, v)] -> Maybe v
lookup "a" [("a", 1), ("b", 2)] = Just 1
lookup "c" [("a", 1), ("b", 2)] = Nothing

如果找不到钥匙,它可能会返回“Nothing”,否则会返回“Just value”。

因此,您可以使用以下内容在这两种数据类型之间进行转换:

convert :: T -> MyEntry
convert (Cons e i fields) = MyEntry i [auth] addr
  where auth = fromMaybe "" (lookup "author" fields)
        addr = fromMaybe "" (lookup "address" fields)

在这里,您将Nothing转换为空字符串,这通常不是一个好主意。因此,您可能需要将地址类型从“String”更改为“Maybe String”,以考虑原始字段中可能缺少地址的事实。

此外,您可能遇到多个作者的经验问题,因为“lookup”会返回第一个匹配键的值,而这就是全部。要处理多个“作者”字段,您可以编写自定义查找函数或将字段列表转换为Data.Map,连接相同键的值。以下是如何(我已将地址类型更改为列表,以便允许多个地址):

data MyEntry = MyEntry {
    ident :: String,
    author :: [String],
    address :: [String]
}

convert :: T -> MyEntry
convert (Cons e i fields) = MyEntry i auth addr
  where
    fieldsMap = fieldsToMap fields
    auth = lookupField "author" fieldsMap
    addr = lookupField "address" fieldsMap

-- lookup field and return an empty list if not found
lookupField :: String -> Map.Map String [String] -> [String]
lookupField = Map.findWithDefault []

-- convert each value into a list and then turn into a map,
-- concatenating values with the same keys
fieldsToMap :: [(String, String)] -> Map.Map String [String]
fieldsToMap = Map.fromListWith (++) . map (\ (k, v) -> (k, [v]))

答案 1 :(得分:2)

您在记录上模式匹配的方式与编写记录文字的方式相同,只使用模式而不是表达式:Constructor { field1 = pat, field2 = pat, ... }。例如:

toEntry Cons{entryType=et, identifier=i, fields=fs} = toEntry' et i fs

toEntry' et i fs = MyEntry { ident = ..., author = ..., address = ... }

您还可以将标准构造函数语法与记录一起使用。在这种情况下,省略辅助函数可能更清楚,而是使用标准语法:

toEntry (Cons et i fs) = MyEntry (...) (...) (...)

另一种选择是使用record puns(带{-# LANGUAGE NamedFieldPuns #-}),它可以省略模式(或记录文字中的表达式),将字段绑定到其名称:

toEntry Cons{entryType, identifier, fields} = ...

此外,由于您绑定了所有字段,因此可以使用record wildcards{-# LANGUAGE RecordWildCards #-}),它只是将所有字段绑定到其名称:

toEntry Cons{..} = ...

然而,在这种情况下,没有足够的字段,普通的构造函数语法对我来说似乎很好,我通常不喜欢记录双关语,因为它们影响字段访问器(与字段具有相同的名称)。