Haskell 两个列表中有多少元素是相同的,并且有重复的元素

Haskell 两个列表中有多少元素是相同的,并且有重复的元素,haskell,Haskell,我试图找出两个列表中相同的元素数量两个列表中有重复的元素 我想要的是: -- (because there are two 's' in both lists ) duplicateEle "sssf" "ssah" = 2 -- (because there are two 'a' and one 's' in both lists, intotal 3 common elements) duplicateEle "aass" &q

我试图找出两个列表中相同的元素数量两个列表中有重复的元素

我想要的是:

-- (because there are two 's' in both lists )
duplicateEle "sssf" "ssah" = 2

-- (because there are two 'a' and one 's' in both lists, intotal 3 common elements)
duplicateEle "aass" "aaas" = 3

-- (because there are two 'a' and two 's' in both lists, intotal 4 common elements)
duplicateEle "ssaa" "ssaa" = 4
我的策略是检查列表1中的每个元素,看看它是否是列表2中的元素

  • 如果列表1的每个元素都是列表2的元素
  • 如果为true,计数1删除第二个列表中的相应元素(Data.List)
比如说,

输入“dddd”“ssdd”输出2,因为两个列表中都有两个
d

首先,我检查列表1中的第一个元素
d
是否是列表2中的元素,结果为True,因此我只删除了列表2中的一个d,count+1,现在count为1

然后我检查列表1中的第二个元素是
d
是否是列表2中的元素,结果也是真的,因此,我在列表2中删除了一个d,count+1,现在count是2

因为列表2中没有剩余的
d
,所以计数将保持在2

我的代码是:(错)


如果您只想在两个包含重复项的列表之间查找公共元素的数量,只需执行以下操作:

f x y = length $ nub $ intersect x y
intersect
将找到公共元素(重复*),而
nub
将从该列表中获得不同的值

注意:
intersect
将只包括第一个参数的重复,即
intersect“ss”s
将返回“ss”,但
intersect“s”ss
将只返回“s”

编辑:根据澄清,我们可以使用
foldl
获得预期结果,如下所示:

dup x y = fst $ foldl (\acc z -> if z `elem` (snd acc) then ((1 + fst acc), delete z (snd acc)) else acc) (0,y) x

这适用于问题中概述的策略-如果在第二个列表的当前值中找到元素,则增加计数并修改第二个列表,否则什么也不做。

如果您只想在两个列表之间找到具有重复项的公共元素数,您可以简单地执行以下操作:

f x y = length $ nub $ intersect x y
intersect
将找到公共元素(重复*),而
nub
将从该列表中获得不同的值

注意:
intersect
将只包括第一个参数的重复,即
intersect“ss”s
将返回“ss”,但
intersect“s”ss
将只返回“s”

编辑:根据澄清,我们可以使用
foldl
获得预期结果,如下所示:

dup x y = fst $ foldl (\acc z -> if z `elem` (snd acc) then ((1 + fst acc), delete z (snd acc)) else acc) (0,y) x

这适用于问题中概述的策略-如果在第二个列表的当前值中找到元素,则增加计数并修改第二个列表,否则什么都不做。

我相信,这就是您打算写的内容

import Data.List

duplicateEleCount :: [Char] -> [Char] -> Int
duplicateEleCount (x:xs) ys = 
    let (count, ys') = if x `elem` ys then (1, delete x ys) else (0, ys)
    in count + duplicateEleCount xs ys'
duplicateEleCount [] _ = 0
你不能像以前那样使用
do
。请记住,Haskell中的所有变量都是不可变的,因此
delete
不会更改原始列表,它会返回一个新的列表,我们必须将其传递给递归调用

关于性能的一点说明:这个函数是
O(n*m)
,因为我们必须为第一个列表中的每个元素遍历整个第二个列表。我们可以先对列表进行排序,然后从merge sort执行类似于合并操作的操作,将其降到
O(n*log(n)+m*log(m))

另一方面,由于haskell的懒惰,我们可以像这样将函数拆分为一个函数,而不会损失任何性能并获得灵活性:

import Data.List

duplicateElems :: [Char] -> [Char] -> [Char]
duplicateElems (x:xs) ys = 
    if x `elem` ys
      then x : duplicateElems xs (delete x ys)
      else duplicateElems xs ys
duplicateElems [] _ = []
 
duplicateEleCount xs ys = length $ duplicateElems xs ys

我相信,这就是你打算写的

import Data.List

duplicateEleCount :: [Char] -> [Char] -> Int
duplicateEleCount (x:xs) ys = 
    let (count, ys') = if x `elem` ys then (1, delete x ys) else (0, ys)
    in count + duplicateEleCount xs ys'
duplicateEleCount [] _ = 0
你不能像以前那样使用
do
。请记住,Haskell中的所有变量都是不可变的,因此
delete
不会更改原始列表,它会返回一个新的列表,我们必须将其传递给递归调用

关于性能的一点说明:这个函数是
O(n*m)
,因为我们必须为第一个列表中的每个元素遍历整个第二个列表。我们可以先对列表进行排序,然后从merge sort执行类似于合并操作的操作,将其降到
O(n*log(n)+m*log(m))

另一方面,由于haskell的懒惰,我们可以像这样将函数拆分为一个函数,而不会损失任何性能并获得灵活性:

import Data.List

duplicateElems :: [Char] -> [Char] -> [Char]
duplicateElems (x:xs) ys = 
    if x `elem` ys
      then x : duplicateElems xs (delete x ys)
      else duplicateElems xs ys
duplicateElems [] _ = []
 
duplicateEleCount xs ys = length $ duplicateElems xs ys

你写的不是那么草率。由于是字符串,我们可以对它们进行排序,然后分组:

import Data.List

-- group :: Eq a => [a] -> [[a]]   -- Defined in `Data.List'

dupreps :: String -> String -> Int
dupreps a b = r
  where
     x = group $ sort a
     y = group $ sort b
现在我们已经将它们进行了排序和分组,我们可以以一种明显的方式沿着这两个列表前进

     r = merge'n'count x y 0

     merge'n'count    _      []      cnt = cnt
     merge'n'count   []      _       cnt = cnt
     merge'n'count   (g:gs)  (f:fs)  cnt 
       | head g == head f  
         =  merge'n'count  gs   fs  (cnt + min (length g) (length f))
       | head g <  head f  
         =  merge'n'count  gs (f:fs) cnt
       | head g >  head f  
         =  merge'n'count (g:gs) fs  cnt

merge'n'count
中的组
g
f
在构造上总是非空的,因此使用
head
是可以的。

你写的不是那么简单。由于是字符串,我们可以对它们进行排序,然后分组:

import Data.List

-- group :: Eq a => [a] -> [[a]]   -- Defined in `Data.List'

dupreps :: String -> String -> Int
dupreps a b = r
  where
     x = group $ sort a
     y = group $ sort b
现在我们已经将它们进行了排序和分组,我们可以以一种明显的方式沿着这两个列表前进

     r = merge'n'count x y 0

     merge'n'count    _      []      cnt = cnt
     merge'n'count   []      _       cnt = cnt
     merge'n'count   (g:gs)  (f:fs)  cnt 
       | head g == head f  
         =  merge'n'count  gs   fs  (cnt + min (length g) (length f))
       | head g <  head f  
         =  merge'n'count  gs (f:fs) cnt
       | head g >  head f  
         =  merge'n'count (g:gs) fs  cnt

merge'n'count
中的组
g
f
在构造上总是非空的,因此使用
head
是可以的。

谢谢shree.pat18,您的解决方案太棒了。您的策略通常是可以的,但是使用
do
在语法上是不正确的。此外,delete函数只删除列表中的第一个实例,因此如果在两个列表中重复相同的字符,最终仍然会重复计数是的,我实现了
do
部分,因为在“if x then y else z”中y和z的类型应该相同。但是如果我想要两个运算,我怎么做呢,还有其他语句吗?关于
列表
部分,由于我删除了第二个列表中的元素,因此不需要重复计算,因为元素将加倍计数已删除。我不确定您提到的“重复计算”是否正确。
delete'a'“abca”
返回“bca”而不是“bc”,因此,如果您的第一个列表是“aab”,即使在为第一个实例“a”调用delete之后,第二个列表中还有另一个“a”,它将与第一个字符串中的第二个“a”相匹配。
Prelude Data.list>length$nub$intersect“aaaa”“ssaa”
Hi shree,当输入为
aaaa时,我发现了一个bug