替换列表中的元素

时间:2019-05-01 07:05:36

标签: haskell compiler-errors fold type-mismatch map-function

我需要实现一个替换列表中元素的功能- 要替换的索引是元组中的void AppName::on_mb_itemname_selected() { // Change the visible child of the stack concerned. Gtk::StackTransitionType ttype = STACK_TRANSITION_TYPE_NONE; _app_stack->set_visible_child("your_widget_name",ttype); // Note that widget name is not widget glade id. // You can set your widget under name Packing -> Name return; } 和元组中的fst 是用它代替的东西。并要求我使用sndfoldr函数。

例如:

map

setElements [(1, 'a'), (-4, 't'), (3, 'b')] "#####" = "#a#b#" 函数无法编译:

我的代码:

setElements

我得到:

setElement :: Int -> a -> [a] -> [a]
setElement n x xs = if ((n < length xs) && n >= 0)
                    then (take n xs) ++ [x] ++ (drop (n + 1) xs)
                    else xs

setElements :: [(Int, a)] -> [a] -> [a]
setElements = foldr (\t l-> setElement (fst t) (snd t) l) [] 

如何解决该错误?

1 个答案:

答案 0 :(得分:4)

让我们看看您的功能:

setElements :: [(Int, a)] -> [a] -> [a]
setElements = foldr (\t l-> setElement (fst t) (snd t) l) []

并回想foldr的类型:

foldr :: (a -> b -> b) -> b -> [a] -> b

在使用foldr时,您将a设置为(Int, a),将b设置为[a]。而且您只给它前两个参数。因此foldr (\t l-> setElement (fst t) (snd t) l) []的类型为[(Int, a)] -> [a]-而setElements的类型应该为[(Int, a)] -> [a] -> [a]。请注意,这些错误与GHC在错误消息中报告的“实际类型”和“预期类型”如何完全匹配。

要解决此问题,我实际上会退后一步。折叠是正确的主意-您的setElement函数已经基于索引和新值修改了原始列表(第三个参数),而您想要的是获取编码此数据对的列表,并继续应用此功能可重复更新原始列表。 (当然,这是Haskell,因此数据是不可变的-您并不是从字面上更新数据,而是每次都返回一个新列表。但是有时候像这样松散地交谈会更容易。)

这就是折痕。让我们尝试将其写出,而不是尝试使用“无点”方法,而应完全应用它:

setElements :: [(Int, a)] -> [a] -> [a]
setElements ps as = foldr myFunction as ps
    where myFunction = undefined

这里的undefined只是一个占位符-如果您尝试使用该函数,则会导致运行时错误(但不会导致编译错误),我将其放在此处是因为我们需要考虑一下,折叠功能通常是实现折叠的最棘手的部分。但是让我们检查一下我们是否理解其他术语在做什么:我们实际上在“走动”的列表是(Int, a)术语的列表,这些术语告诉我们要插入的内容以及插入的位置-这就是我所说的{{1} }(ps用于“ pair”)。而且因为我们从p的列表开始-我在这里逻辑上将其称为a-所以它应该是起始累加器值,它是as的第二个参数。

因此,剩下的只是fold函数-它接受一对和一个列表,并根据该对中的值更新列表。嗯,这就是您已经在使用的功能:

foldr

或者,用模式匹配重写(我发现它更易读,因此,我认为大多数Haskell开发人员更喜欢这种模式):

\t l-> setElement (fst t) (snd t) l

因此,将其代入以下定义:

\(i, a) as -> setElement i a as

现在,它将编译并正常工作。但是,当您具有工作功能时,总是值得退后一步,看看是否可以简化其定义。实际上setElements :: [(Int, a)] -> [a] -> [a] setElements ps as = foldr myFunction as ps where myFunction = \(i, a) as -> setElement i a as 可以简化很多:

myFunction

可以首先“简化为”

\(i, a) as -> setElement i a as

,使用标准库函数,其简称为\(i, a) -> setElement i a

在此阶段,我们显然不再需要uncurry setElement子句(事实上,我们以前从未这样做过,但imo可以帮助任何不太琐碎的lambda读取),并且可以编写:

where

实际上,虽然我不一定会推荐它,但如果我们正在打代码高尔夫,您甚至可以走得更远,只需编写:

setElements :: [(Int, a)] -> [a] -> [a]
setElements ps as = foldr (uncurry setElement) as ps

我个人认为,如上所述,能够以简洁的方式表达相对复杂的功能的能力绝对是Haskell魅力的一部分。但是,我认为与其尝试立即写出这样的内容,不如直接从一个非常具体的例子开始,以显示如何转换数据,并且最好在工作之后,寻找一个更简洁的表示形式。想。