我想将bibtex entry转换为我自己的自定义数据类型,类似于:
date MyEntry = MyEntry {
ident :: String,
author :: [String],
address :: String
}
但author
和address
等字段存储在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, ???????? }
答案 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{..} = ...
然而,在这种情况下,没有足够的字段,普通的构造函数语法对我来说似乎很好,我通常不喜欢记录双关语,因为它们影响字段访问器(与字段具有相同的名称)。