Function Haskell:多集(Bag)表示

Function Haskell:多集(Bag)表示,function,haskell,functional-programming,multiset,bag,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]

目前,我正在尝试获取整数列表(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,参数为当前元素和包。对于下一个元素,执行相同的操作-传递该元素,以及从上一个add调用返回的包

我不确定的是:我该如何编写代码?我已经把我的(不太好的)尝试写在下面了。我已经完全弄懂了算法,但不确定如何执行。任何方向正确的建议都会很好

multi :: Eq a => [a] -> Bag a
multi [] = []
multi (x:xs) = ins x []

正如你所说,你已经找到了算法;您几乎可以直接将其转换为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
传递两个参数,而不是
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
这就解决了这个包的问题,它只是传递给
的任何东西都是相同的。但是现在我们有一些
占位符需要填充一些东西。处理完元素后,
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
那么第一次给doTheSame的
呢?那一定是你开始的包,大概是一个空包。(你需要自己定义这是什么。)

假设您有
add
emptyBag
的定义,则此代码有效!但你可能想把它整理一下。更有经验的Haskell程序员可能会使用一些较短的名称,并将
returnedBag
内联:

-- 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)
这段代码与前一段代码完全相同。选择其中一段代码的唯一原因是您是否发现其中一段代码更易于阅读。(永远不要低估阅读自己代码的重要性,也不要高估自己在一段时间后阅读代码的能力,因为它在你的脑海中已经不再新鲜!)


额外学分:

这种递归通常在函数式语言中非常常见,通常称为折叠。折叠从一些数据(在本例中为空包)开始,遍历列表或类似列表的结构,对于该结构中的每个元素,使用函数(在本例中为add)将数据与元素组合,生成新数据,用于下一个元素,依此类推,返回数据的最终值(在本例中,是一个包含所有元素的包)。由于这是一种常见的模式,在Haskell中,有一个名为
foldl
(用于左折叠,因为您从左侧开始处理列表元素)的函数,它只需要一个组合函数、一个初始值和一个列表,并为您完成所有其他工作:

multi :: Eq a => [a] -> Bag a
multi elts = foldl (\bag x -> add x bag) emptyBag elts

当你还在学习递归和Haskell的基础知识时,我不会试图用上次版本的
multi
的风格来编写代码。但是一旦你做了几次
where go
技巧,并且厌倦了每次都写出来,就去查阅
foldl
foldr
和t进入下一步!

这是一个典型的
折叠情况。
。如果你可以依赖
Ord
,首先对它们进行排序将使实现更干净,而且比单独使用
Eq
更有效。@WillemVanOnsem我将非常快地查看折叠。@Ry不幸的是,这是一个函数我必须使用的声明。提示:您不想将
x
插入一个空包中,您要将它插入已经包含
xs
内容的包中——也就是说,由
multi-xs
构建的包。
multi :: Eq a => [a] -> Bag a
multi elts = foldl (\bag x -> add x bag) emptyBag elts