计算两个字符串之间的差异

时间:2017-08-25 13:05:35

标签: list haskell

我有两个字符串

a :: [String]
a = ["A1","A2","B3","C3"]

b :: [String]
b = ["A1","B2","B3","D5"]

我想基于第一个字符和第二个字符以及两个字符的组合计算两个字符串之间的差异。 如果两个元素的组合相同,则计算为1

我声明的函数是

calcP :: [String] -> [String] -> (Int,[String])
calcP (x:xs) (y:ys) = (a,b)
  where
    a = 0 in
      ???
    b = ????

我知道我应该有一个增量变量来计算正确的元素,我应该把它放在哪里?现在我完全不知道怎么做,有人能给我一些暗示吗?

期望的结果是

(2,["B2","D5"])

我该怎么做?

3 个答案:

答案 0 :(得分:1)

递归地做什么呢?如果两个元素相同,则生成的元组的第一个元素递增;否则,生成的元组的第二个元素会被不匹配的元素追加:

calcP :: [String] -> [String] -> (Int,[String])
calcP (x:xs) (y:ys)
  | x == y = increment (calcP xs ys)
  | otherwise = append y (calcP xs ys)
  where
    increment (count, results) = (count + 1, results)
    append y (count, results) = (count, y:results)

calcP [] x = (0, x)
calcP x [] = (0, [])

a = ["A1","A2","B3","C3"]
b = ["A1","B2","B3","D5"]

main = print $ calcP a b

打印结果为(2,["B2","D5"])

注意,

calcP [] x = (0, x)
calcP x [] = (0, [])

需要为模式匹配提供详尽的信息。换句话说,您需要提供其中一个传递的元素是空列表的情况。这也提供了以下逻辑:

如果第一个列表大于n元素上的第二个列表,则忽略这些n个最后一个元素。

如果第二个列表大于n元素上的第一个列表,则这些n个最后一个元素将附加到结果元组的第二个元素。

答案 1 :(得分:1)

我认为这些列表的大小相同。

两个列表之间的差异

让我们关注问题的主要部分:

Prelude> a=["A1","A2","B3","C3"]
Prelude> b=["A1","B2","B3","D5"]

首先,请注意zip方法会压缩两个列表。如果您在ab上使用它,则会获得:

Prelude> zip a b
[("A1","A1"),("A2","B2"),("B3","B3"),("C3","D5")]

确定。现在是时候比较一对一的条款了。有很多方法可以做到。

过滤

Prelude> filter(\(x,y)->x/=y)(zip a b)
[("A2","B2"),("C3","D5")]

如果对的元素不同(/=运算符),则lambda函数返回True。因此,过滤器仅保留不匹配的对。 没关系,但是你必须做更多的工作才能保留每对中的第二个元素。

Prelude> map(snd)(filter(\(x,y)->x/=y)(zip a b))
["B2","D5"]

map(snd)snd应用于每个不一致的对,只保留一对中的第二个元素。

折叠

折叠更通用,可用于实现过滤器。我们来看看如何:

Prelude> foldl(\l(x,y)->if x==y then l else l++[y])[](zip a b)
["B2","D5"]

lambda函数接受每对(x,y)并比较这两个元素。如果它们具有相同的值,则累加器列表保持相同,但如果值不同,则累加器列表将由第二个元素扩充。

列表理解

这个更紧凑,对每个Python程序员来说都应该是显而易见的:

Prelude> [y|(x,y)<-zip a b, x/=y] -- in Python [y for (x,y) in zip(a,b) if x!= y]
["B2","D5"]

元素数量

您想要一对具有元素数量和元素本身的对。

折叠

使用折叠,它很容易但很麻烦:您将使用稍微复杂的累加器,它同时存储差异(l)和差异的数量(n)。

Prelude> foldl(\(n,l)(x,y)->if x==y then (n,l) else (n+1,l++[y]))(0,[])$zip a b
(2,["B2","D5"])

LAMBDA

但是你可以使用你的输出是多余的这一事实:你想要一个列表,前面是该列表的长度。为什么不应用一个完成这项工作的lambda?

Prelude> (\x->(length x,x))[1,2,3]
(3,[1,2,3])

通过列表理解,它给出了:

Prelude> (\x->(length x,x))[y|(x,y)<-zip a b, x/=y]
(2,["B2","D5"])

绑定运算符

最后,为了好玩,您不需要以这种方式构建lambda。你可以这样做:

Prelude> ((,)=<<length)[y|(x,y)<-zip a b,x/=y]
(2,["B2","D5"])

这里发生了什么? (,)是一个运算符,它由两个元素组成:

Prelude> (,) 1 2
(1,2)

((,)=<<length):1。获取一个列表(技术上为Foldable)并将其传递给length函数; 2.然后,=<<(“绑定”运算符)将列表和长度传递给(,)运算符,从而得到预期的结果。

部分结论

  • “有不止一种方法可以做到”(但它不是Perl!)
  • Haskell提供了许多内置函数和运算符来处理这种基本操作。

答案 2 :(得分:0)

我想提出一种与其他人不同的方法:即,为两个列表之间的每个元素配对计算“汇总统计量”,然后将汇总结合到您想要的结果中。

首先进口一些。

import Data.Monoid
import Data.Foldable

对我们来说,摘要统计数据是有多少匹配,以及第二个参数的不匹配列表:

type Statistic = (Sum Int, [String])

我使用Sum Int代替Int来指定统计信息的组合方式。 (此处的其他选项包括Product Int,它会将值相乘而不是添加它们。)我们可以非常简单地计算单个配对的摘要:

summary :: String -> String -> Statistic
summary a b | a == b    = (1, [ ])
            | otherwise = (0, [b])

组合所有元素的摘要只是fold

calcP :: [String] -> [String] -> Statistic
calcP as bs = fold (zipWith summary as bs)

在ghci:

> calcP ["A1", "A2", "B3", "C3"] ["A1", "B2", "B3", "D5"]
(Sum {getSum = 2},["B2","D5"])

这种一般模式(将元素一次处理成Monoid类型)通常很有用,并且发现它适用的位置可以大大简化代码。