2017-03-16 46 views
0

所以我有一个自定义数据类型Person记录中更新一个值,记录的集合哈斯克尔

data Person = Person{ fname :: String 
         , lname :: String 
         , age :: Int 
         , siblings :: [String] 
         } 

我有这种类型的列表中,foo = [Person].

我想更新一个特定的Person。我的过程是匹配它们的fname(假设每个名称都是唯一的),然后更新它们的siblings值。

addSiblingToPerson :: String -> String -> [Person] -> [Person] 
addSiblingToPerson siblingParam fnameParam fooParam = 

我真的很努力想“功能”,如果我这样做是命令式语言,我可以去通过每个项目[Person]检查,看看if name == fname then this.siblings push newSibling(或者类似的规定)

我知道如何更新haskell中的记录,但是我想在更新Person集合中的单个人之后返回Person的列表。

我只是不能换我围​​绕如何头“认为哈斯克尔”

谢谢:(

+1

发现令人兴奋的[镜头]世界(https://hackage.haskell.org/package/lens)。 – arrowd

回答

1

你不应该去想“更新”的东西,即使我们使用的术语,除非你有一个可变的参考,并在IO monad中工作,在这种情况下,思考过程应该是“我如何计算一个新的列表,它与前面的列表完全相同,除了...”

您可以更新单个条目或map整个列表中的修改功能。让我们看看手册,单个条目,soluti在第一次:

addSiblingToPerson :: String -> String -> [Person] -> [Person] 
addSiblingToPerson siblingParam fnameParam allPeople = 
    case allPeople of 
     []  -> [] 
     (p:ps) | fname p == fnameParam -> 
        p { siblings = siblingParam : siblings p } : ps 
       | otherwise -> 
        p : addSiblingToPerson siblingParam fnameParam ps 

也就是说,我们遍历列表,保留任何不匹配的人以及更新的第一人具有匹配fname,请务必提供列表的其余部分。

的地图解决方案在功能上是不同的 - 它会更新谁分享给所有fname,它会遍历整个列表。

addSiblingToPerson :: String -> String -> [Person] -> [Person] 
addSiblingToPerson siblingParam fnameParam allPeople = 
     let update p | fname p == fnameParam = p { siblings = siblingParam : siblings p } 
        | otherwise = p 
     in map update allPeople 
+0

谢谢。 “我如何计算一个与之前完全一样的新列表,除了......”。真的帮助我理解它。当然,创建一个全新的列表只是为了修改一个值,但效率不高? (并不是说你的回答不令人满意,这很好,真的帮助我理解),但更多地指向了Haskell。 – ThatGuyOverThereIsMe

+0

首先:你是对的。当然,在这种情况下,如果您希望获得良好的性能,列表并不是您想要使用的结构。第二:不要太早挂断你认为有效的东西。在这个例子中,第一次匹配后的列表部分将被共享 - 更新前列表和更新后列表都有指向公共尾部的指针。在Haskell中,大多数值都是装箱的,更新操作涉及的是将指针移动到旧结构,旧元素等。 –

+0

请注意,即使您可以对列表进行变更,效率也会很低。您需要执行O(n)工作来查找需要更新的元素,因此执行额外的O(n)工作来复制不匹配的元素并不是什么大不了的事情。 – amalloy