当前,我正在尝试获取一个整数列表(Int),并将它们放入多集表示形式中。对于一些背景,表示形式如下所示:
*user passes in a list:* [1,2,3,4]
*multiset representation:* [(1,1),(2,1),(3,1),(4,1)]
我编写了两个函数: add 和 del 。 add 接受一个整数和一个袋子,然后将整数插入袋子。它检查重复项-如果存在,则仅将计数器(包中坐标的第二个元素)加1。然后返回该包。
所以,我的算法应该是:取列表,说[1,2,3,4];遍历列表中的每个元素-并针对每个元素调用 add ,并将参数作为当前元素和bag。对于下一个元素,请执行相同操作-将元素与上一个 add 调用返回的包一起传递。
这就是我的想法:我该如何实际编码?我将我的(不太好)尝试放在了下面。我已经弄清楚了算法,但是不确定如何执行。正确方向上的任何技巧都很棒。
multi :: Eq a => [a] -> Bag a
multi [] = []
multi (x:xs) = ins x []
答案 0 :(得分:1)
正如您所说,您已经找到了算法;您几乎可以将其直接翻译为Haskell:
-- So, my algorithm should be: take the list, say [1,2,3,4];
multi :: Eq a => [a] -> Bag a
-- go through each element in the list
multi [] = []
multi (x:xs) =
-- and for each of these elements, call add with the parameters being the current element
-- and the bag.
let returnedBag = add x theBag
-- For the next element, do the same - pass the element, with the bag that was returned
-- from the previous add call.
in doTheSame xs returnedBag
当然,这并不是很有效,因为缺少两个定义:doTheSame
是什么,theBag
是什么?好吧,我们希望doTheSame
现在表示multi
主体中的所有内容,但是请注意,我们想将两个参数传递给doTheSame
而不是{{1} }。因此,让我们尝试将multi
设为自己的函数:
doTheSame
这解决了-- So, my algorithm should be: take the list, say [1,2,3,4];
multi :: Eq a => [a] -> Bag a
-- go through each element in the list
multi elts = doTheSame elts ???
where
doTheSame [] theBag = ???
doTheSame (x:xs) theBag =
-- and for each of these elements, call add with the parameters being the current element
-- and the bag.
let returnedBag = add x theBag
-- For the next element, do the same - pass the element, with the bag that was returned
-- from the previous add call.
in doTheSame xs returnedBag
是什么的问题-就是传递给theBag
的任何东西。但是现在我们那里有一些doTheSame
占位符,需要用一些东西来填充。 ???
处理完元素后应该做什么?毫无疑问,退回它一直在建造的袋子:
multi
那首先给-- So, my algorithm should be: take the list, say [1,2,3,4];
multi :: Eq a => [a] -> Bag a
-- go through each element in the list
multi elts = doTheSame elts ???
where
doTheSame [] theBag = theBag
doTheSame (x:xs) theBag =
-- and for each of these elements, call add with the parameters being the current element
-- and the bag.
let returnedBag = add x theBag
-- For the next element, do the same - pass the element, with the bag that was returned
-- from the previous add call.
in doTheSame xs returnedBag
的{{1}}呢?那一定是您开始使用的袋子-大概是一个空袋子。 (您需要定义适合自己的内容。)
???
假定您具有doTheSame
和-- So, my algorithm should be: take the list, say [1,2,3,4];
multi :: Eq a => [a] -> Bag a
-- go through each element in the list
multi elts = doTheSame elts emptyBag
where
doTheSame [] theBag = theBag
doTheSame (x:xs) theBag =
-- and for each of these elements, call add with the parameters being the current element
-- and the bag.
let returnedBag = add x theBag
-- For the next element, do the same - pass the element, with the bag that was returned
-- from the previous add call.
in doTheSame xs returnedBag
的定义,此代码将起作用!但是您可能需要整理一下。经验丰富的Haskell程序员可能会使用一些较短的名称,并内嵌add
:
emptyBag
此代码与之前的代码完全相同-相对于另一个而言,偏爱一个的唯一原因是您是否发现其中一个更易于阅读。 (永远不要低估能够读取自己的代码的重要性,并且永远不要过分高估在经过一段时间之后再读下去的能力,而且这在您心中不再新鲜!)
额外功劳:
通常,这种递归在函数式语言中非常常见,通常称为 fold 。折叠从一些数据(在这种情况下为一个空袋子)开始,遍历一个列表或类似列表的结构,对于该结构中的每个元素,使用一个函数(在这种情况下为add)将数据与元素以生成新数据,该数据将用在下一个元素上,依此类推,以返回数据的最终值(在这种情况下,是一个包含所有元素的包)。由于这是一种常见的模式,因此在Haskell中,有一个名为returnedBag
的函数(用于 left fold ,因为您要从左开始处理列表元素)只需要合并即可函数,一个初始值和一个列表,然后为您完成所有其余工作:
-- So, my algorithm should be: take the list, say [1,2,3,4]; go through each element in the
-- list - and for each of these elements, call add with the parameters being the current
-- element and the bag. For the next element, do the same - pass the element, with the bag
-- that was returned from the previous add call.
multi :: Eq a => [a] -> Bag a
multi elts = go elts emptyBag
where go [] bag = bag
go (x:xs) bag = go xs (add x bag)
尽管您仍在学习递归和Haskell的基础知识,但我不会尽全力以最后一种foldl
的样式编写代码。但是一旦您完成了multi :: Eq a => [a] -> Bag a
multi elts = foldl (\bag x -> add x bag) emptyBag elts
的技巧几次,并且厌倦了每次都写完所有这些内容,就去查找multi
和where go
并继续下一步吧!>