Haskell:多集(包)表示

时间:2019-04-13 22:29:57

标签: function haskell functional-programming multiset bag

当前,我正在尝试获取一个整数列表(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 []

1 个答案:

答案 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 的技巧几次,并且厌倦了每次都写完所有这些内容,就去查找multiwhere go并继续下一步吧!