在 Haskell 中添加列表/覆盖现有列表的功能

Posted

技术标签:

【中文标题】在 Haskell 中添加列表/覆盖现有列表的功能【英文标题】:Functionality of adding to lists in Haskell / overwriting an existing List 【发布时间】:2014-01-01 06:23:28 【问题描述】:
type Dictionary = [(String, String)]

dict :: Dictionary
dict = ("Deutsch", "English"):[]

insert :: Dictionary -> (String,String) -> Dictionary
insert dict entry = dict ++ [entry]

我没有找到关于列表工作方式的一件事:是否有可能用插入中添加的条目覆盖现有的字典?或者是不是必须在下一步中,总是写出被insert放出来的list?

insert [("German", "English"), ("Hallo", "hello")] ("Versuch", "try")

到目前为止,这是我能够在不丢失先前条目的情况下向新列表中添加内容的唯一方法。但是,接下来要实现的是一个搜索命令,所以我想知道我是否也必须在搜索函数中写出来。

【问题讨论】:

“写出来”到底是什么意思?顺便说一句,插入函数的右手边会更好entry:dict 如果你正在创建一个关联列表,如果你在列表的前面插入,那么你不需要删除之前的值,因为任何搜索都会找到最近添加的项目。跨度> 我已经尝试过使用 cons 运算符,但我的列表似乎没有关联列表?因为当我将运算符从 ++ 更改为 : 时遇到了同样的问题,所以我得到的只是一个额外的条目,而没有像 RasmusWriedtLarsen 的示例中那样重命名所有内容。 @Ingo 我的意思不是用 let dict2 等给新列表名称,而是像我在问题中写的插入命令一样写整个列表及其条目。 【参考方案1】:

函数式编程的理念通常是您的数据是不可变的。这意味着一旦您创建了一个列表,就永远不能更改该列表。但是您可以复制该列表,对其进行修改并保留它。

所以当你有这样的列表时

test = [1,2,3]

我们可以通过在开头添加4 来修改它:

test2 = 4 : test

: 称为 cons 运算符,将一个元素放在列表的前面。请注意x:xs(与[x]++xs 相同)比xs++[x] 具有更好的性能

所以现在我们有两个绑定,一个是test[1,2,3],一个是test2[4,1,2,3]

希望这能澄清一些事情


举一个完整的例子:

type Dictionary = [(String, String)]

insert :: Dictionary -> (String,String) -> Dictionary
insert dict entry = dict ++ [entry]

dict0 = [ ("Deutsch", "English") ]
dict1 = insert dict0 ("Hallo", "hello")
dict2 = insert dict1 ("Versuch", "try")

如果您是函数式编程的新手,我建议您阅读 Learn You a Haskell for Great Good,这是一本关于如何使用 Haskell 以及一般的函数式编程的精彩(免费)书籍。

【讨论】:

如果我尝试在命令行中执行此操作,我必须先添加 let ,对吗?喜欢let dict1 = insert dict0 ("Hallo", "Hello")? dict ++ [entry] -- 在列表末尾添加一个元素在性能方面非常糟糕。为什么不直接使用冒号运算符? 我认为将标题(“Deutsch”,“English”)放在列表​​的头部会很聪明,如果我添加到列表的开头,头部会不断变化,不是吗? @user2894391 @dschib 换头是最便宜的列表操作,所以最好是这样。 如果我在新元素前面加上:,难道我不需要永久这样做吗?或者有没有办法让头粘住?【参考方案2】:

做到这一点并不难

import Data.List (lookup)

insert :: Eq a => (a,b) -> [(a,b)] -> [(a,b)]
insert (a,b)  []           = [(a,b)]
insert (a,b) ((c,d):rest) = if a == c
    then (a,b) : rest
    else (c,d) : insert (a,b) rest

---

dict :: [(String, String)]
dict = [("Deutsch", "English")]

如果你不能使用Data.List,那么你可以定义lookup

lookup :: Eq a => a -> [(a,b)] -> Maybe b
lookup _  []          = Nothing
lookup k ((a,b):rest) = if k == a then Just b else lookup k rest

现在如果你加载 GHCI:

>> let dict' = insert ("Ein","One") dict
>> dict'
[("Deutsch","English"),("Ein","One")]
>> lookup "Ein" dict'
Just "One"
>> insert ("Deutsch", "Francais") dict'
[("Deutsch","Francais"),("Ein","One")]

【讨论】:

这个任务的重点是不要用 Data.List 来做,因为我们现在还没有那么远,仍然需要掌握函数式编程的基础知识。 @dschib 我编辑删除了对Data.List的依赖 非常感谢,查找功能对我有很大帮助!【参考方案3】:

如果你想用相同的键替换现有的对,那么你可以将插入写成:

insert :: Dictionary -> (String, String) -> Dictionary
insert [] p = [p]
insert ((dk, dv):ps) p@(k, v) | dk == k = p:ps
insert (p:ps) ip = p : (insert ps ip)

但是,如果您正在编写关联列表,则可以通过在列表的前面插入新项目来简化它:

insert :: Dictionary -> (String, String) -> Dictionary
insert = flip (:)

如果您随后从列表的前面搜索,它将首先找到最近添加的任何值。

【讨论】:

【参考方案4】:

在 Haskell 中,大多数值是不可变的,这意味着您无法更改它们的值。起初这似乎是一个巨大的限制,但实际上它更容易推理您的程序,尤其是在使用多个线程时。

您可以改为在调用insert 时返回的字典上不断调用insert,例如:

mainLoop :: Dictionary -> IO ()
mainLoop dict = do
    putStrLn "Enter the German word:"
    german <- getLine
    putStrLn "Enter the English word:
    english <- getLine
    let newDict = insert dict (german, english)
    putStrLn "Continue? (y/n)"
    yesno <- getChar
    if yesno == 'y'
        then mainLoop newDict
        else print newDict

main = do

【讨论】:

我很想与 IO 一起工作,但这与我们现在的演讲相去甚远。我们应该做的就是在命令行中使用插入命令,没有提示或任何类似的东西。我在理解这一点时遇到的主要问题是,如果无论如何 dict 不是一成不变的,但我想这就像试图在猪面前扔珍珠。 不是没有 IO。您可以使用 State monad,但如果 IO 超出了您当前的讲座范围,那也是如此。现在你只需要按照@RasmusWriedtLarsen 的建议继续重新绑定它。【参考方案5】:

人们根本无法用纯语言(ST monad 之外)“覆盖”任何内容。如果我正确理解了您的问题,那么您正在寻找这样的东西:

insert :: Dictionary -> (String,String) -> Dictionary
insert [] b = [b]  -- If this point is reached where wasn't matching key in dictionary, so we just insert a new pair
insert (h@(k, v) : t) b@(k', v')
    | k == k'   = (k, v') : t      -- We found a matching pair, so we 'update' its value
    | otherwise = h : insert t b

【讨论】:

在我的任务中不需要检查重复项,我理解的主要问题是看到我定义的每个 dict 是如何不可变的,所以如果我想为一个包含多个条目的字典,我必须使用let dict3 = insert dict2 ("This", "that") @dschib 你可以使用 state monad 来模拟变量。 再说一次,我以后可以查到的东西,我们在讲座中的最后一件事是数组,而 monad 的路还很长……

以上是关于在 Haskell 中添加列表/覆盖现有列表的功能的主要内容,如果未能解决你的问题,请参考以下文章

在 Haskell 中添加存储为 0 和 1 列表的两个二进制数,而不使用反向

Python 等效于 Haskell 的 [1..](索引列表)

带有 Flutter 的实时数据库覆盖数据而不是将其添加到现有节点

Haskell 列表生成器

Haskell:如何附加到元组列表列表?

Haskell 持久列表