【问题标题】:Calculating the difference between two strings计算两个字符串之间的差异
【发布时间】:2017-08-25 13:05:35
【问题描述】:

我有两个字符串

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"])

我该怎么做?

【问题讨论】:

  • 我认为您应该尝试使用递归辅助函数并将“不匹配”的元素放入累加器列表中,此外您应该考虑如果列表长度不同会发生什么。我猜的第一个数字是累加器列表中元素的数量 - 为此使用 length
  • 您的示例也不正确,haskell 代码 - where .. in 不正确。
  • 您能对您想要达到的目标提供更多解释吗?我们从“两个字符串”ab 开始,但这些实际上是字符串列表。有哪些“基于第一个字符和第二个字符以及两个字符组合的两个字符串之间的区别”的例子?

标签: list haskell


【解决方案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?

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)

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

部分结论

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

【讨论】:

    【解决方案2】:

    递归地做呢?如果两个元素相同,则结果元组的第一个元素递增;否则,结果元组的第二个元素将附加不匹配的元素:

    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 最后一个元素将附加到结果元组的第二个元素。

    【讨论】:

    • 非常感谢!太有帮助了!!
    • 这里面的结果有什么作用?
    • results 只是累积差异的列表。在结果元组中是["B2","D5"]
    【解决方案3】:

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

    首先是一些导入。

    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"])
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多