Scala字符串相似性

时间:2016-04-23 09:23:34

标签: string scala similarity

我有一个Scala代码,用于计算一组字符串之间的相似性,并提供所有唯一字符串。

val filtered = z.reverse.foldLeft((List.empty[String],z.reverse)) {
    case ((acc, zt), zz) =>
      if (zt.tail.exists(tt => similarity(tt, zz) < threshold)) acc 
      else zz :: acc, zt.tail
  }._1

我会试着解释一下这里发生了什么:

这使用了反转输入数据的折叠,从空字符串(累积结果)和剩余输入数据(反向)开始(比较 - 我将其标记为zt为“z-tail”)。

然后折叠循环遍历数据,检查每个条目与剩余数据的尾部(因此它不会与自身或任何早期条目进行比较)

如果匹配,则只允许通过现有累加器(标记为acc),否则,将当前条目(zz)添加到累加器。这个更新的累加器与“剩余”字符串(zt.tail)的尾部配对,以确保要比较的缩减集。

最后,我们最终得到一对列表:所需的剩余字符串和一个空列表(没有字符串可供比较),因此我们将第一个作为结果。

问题就像在第一次迭代中一样,如果第1,第4和第8个字符串相似,我只得到第一个字符串。而不是它,我应该得到一组(第1,第4,第8),然后如果第2,第5,第14和第21弦相似,我应该得到一组(第2,第5,第14,第21)。

1 个答案:

答案 0 :(得分:0)

如果我理解正确 - 你希望结果是List[List[String]]类型,而不是你现在得到的List[String] - 每个项目都是类似字符串的列表(对吗?)。 / p>

如果是这样 - 我无法看到对您的实现进行微不足道的更改,因为相似的值丢失(当您输入if(true)分支并返回时) acc - 你跳过一个项目,你永远不会再看到#34;

我能想到的两种可能的解决方案:

  1. 根据您的想法,但使用(acc, zt, scanned)形式的3-Tuple作为foldLeft结果类型,其中添加的scanned是已扫描的列表项目。这样我们就可以在找到一个不具有类似元素的元素时再引用它们:

    val filtered = z.reverse.foldLeft((List.empty[List[String]],z.reverse,List.empty[String])) {
      case ((acc, zt, scanned), zz) =>
        val hasSimilarPreceeding = zt.tail.exists { tt => similarity(tt, zz) < threshold }
        val similarFollowing = scanned.collect { case tt if similarity(tt, zz) < threshold => tt }
        (if (hasSimilarPreceeding) acc else (zz :: similarFollowing) :: acc, zt.tail, zz :: scanned)
    }._1
    
  2. 一个可能更慢但更简单的解决方案就是groupBy类似的字符串组:

    val alternative = z.groupBy(s => z.collect { 
      case other if similarity(s, other) < threshold => other 
    }.toSet ).values.toList
    
  3. 所有这些假设函数:

    f(a: String, b: String): Boolean = similarity(a, b) < threshold
    

    具有交换性和传递性,即:

    • f(a, b) && f(a. c)表示f(b, c)
    • f(a, b)当且仅当f(b, a)

    测试我使用的两种实现:

    // strings are similar if they start with the same character
    def similarity(s1: String, s2: String) = if (s1.head == s2.head) 0 else 100
    
    val threshold = 1
    val z = List("aa", "ab", "c", "a", "e", "fa", "fb")
    

    这两个选项产生相同的结果:

    List(List(aa, ab, a), List(c), List(e), List(fa, fb))