我需要实现一个替换列表中元素的功能-
要替换的索引是元组中的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
是用它代替的东西。并要求我使用snd
或foldr
函数。
例如:
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) []
如何解决该错误?
答案 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魅力的一部分。但是,我认为与其尝试立即写出这样的内容,不如直接从一个非常具体的例子开始,以显示如何转换数据,并且最好在工作之后,寻找一个更简洁的表示形式。想。