Scala-查找两个序列不同的第一个位置

Scala-查找两个序列不同的第一个位置,scala,diff,sequence,seq,Scala,Diff,Sequence,Seq,Scala附带了nice对应的方法: val a = scala.io.Source.fromFile("fileA").getLines().toSeq() val b = scala.io.Source.fromFile("fileB").getLines().toSeq() val areEqual = a.corresponds(b){_.equals(_)} if(areEqual) ... 我很喜欢它的简洁 是否已经定义了一种类似的方法,可以向我报告两个序列不同的第一个位置 也

Scala附带了nice
对应的
方法:

val a = scala.io.Source.fromFile("fileA").getLines().toSeq()
val b = scala.io.Source.fromFile("fileB").getLines().toSeq()

val areEqual = a.corresponds(b){_.equals(_)}

if(areEqual) ...
我很喜欢它的简洁

是否已经定义了一种类似的方法,可以向我报告两个序列不同的第一个位置

也就是说,有没有更惯用的方式来写这样的东西:

val result = ((seqA zip seqB).zipWithIndex).find{case ((a,b),i) => !a.equals(b)} match{
    case Some(((a,b),i)) => s"seqA and seqB differ in pos $i: $a <> $b"
    case _ => "no difference"
}
val result=((seqA-zip-seqB).zipWithIndex).find{case((a,b),i)=>!a.equals(b)}匹配{
案例部分((a,b,i))=>s“seqA和seqB在pos$i中不同:$a$b”
案例=>“无差异”
}
因为正如你所看到的,这是一个血腥的痛苦在脖子上阅读。如果我想用三元组而不是元组中的元组,情况会更糟:

val result = (((seqA zip seqB).zipWithIndex) map {case (t,i) => (t._1,t._2,i)}).find{case (a,b,i) => !a.equals(b)} match{
    case Some((a,b,i)) => s"seqA and seqB differ in pos $i: $a <> $b"
    case _ => "no difference"
}
val result=((seqA-zip-seqB).zipWithIndex)映射{case(t,i)=>(t._1,t._2,i)})。查找{case(a,b,i)=>!a.equals(b)}匹配{
案例部分((a,b,i))=>s“seqA和seqB在pos$i中不同:$a$b”
案例=>“无差异”
}

我知道
diff
方法。不幸的是,这种方法忽略了元素的顺序。

这样更好一些:

(as zip bs).zipWithIndex.collectFirst { case ((a,b),i) if a!=b => i }
见:

如果要在输出中使用
a
b

(as zip bs).zipWithIndex.collectFirst { case ((a,b),i) if a!=b => (i,a,b) }
另外:如果您希望它与您的
示例类似,您可以将其作为扩展方法:

implicit class Enriched_counts_TraversableOnce[A](val as: TraversableOnce[A]) extends AnyVal {
  def firstDiff[B](bs: TraversableOnce[B]): Option[Int] = {
    (as.toIterator zip bs.toIterator)
      .zipWithIndex
      .collectFirst { case ((a,b),i) if a!=b => i }
  }
}

Seq(1,2,3,4).firstDiff(Seq(1,2,9,4))
// res2: Option[Int] = Some(2)
甚至:

implicit class Enriched_counts_TraversableOnce[A](val as: TraversableOnce[A]) extends AnyVal {
  def firstDiff2[B](bs: TraversableOnce[B])(p: (A,B) => Boolean): Option[Int] = {
    (as.toIterator zip bs.toIterator)
      .zipWithIndex
      .collectFirst { case ((a,b),i) if !p(a,b) => i }
  }
}

Seq(1,2,3,4).firstDiff2(Seq(1,2,9,4)){ _ == _ }
// res3: Option[Int] = Some(2)

您可以使用
索引,其中
(请参阅)如下所示:

(as zip bs).indexWhere{case (x,y) => x != y}
as.indices.find(i => as(i) != bs(i))
例如:

scala> val as = List(1,2,3,4)
scala> val bs = List(1,2,4,4)

scala> (as zip bs).indexWhere{case (x,y) => x != y}

res0: Int = 2
但是,请注意,如果一个Seq比另一个长(
zip
截断较长的Seq),则基于
zip
的所有解决方案可能不会报告任何差异-这可能是您需要的,也可能不是您需要的

更新:对于等长的序列,不同的方法如下:

(as zip bs).indexWhere{case (x,y) => x != y}
as.indices.find(i => as(i) != bs(i))
这很好,因为它返回一个
选项[Int]
,因此如果seq之间没有差异,它将返回
None
,而不是神奇的-1

如果
as
bs
短,它的行为与其他解决方案相同,但如果
as
长,它将失败(当然,您可以选择最小长度)

但是,由于它通过索引来处理这两个seq,因此它只能在
IndexedSeq
s中运行良好

更新2:我们可以使用
lift
处理不同的Seq长度,以便在按索引检索元素时获得一个选项:

bs.indices.find(i => as.lift(i) != bs.lift(i))
因此,如果
as=[1,2]
bs=[1,2,3]
,它们不同的第一个索引是2(因为
as
中缺少此元素)。但是,在这种情况下,我们需要对最长的Seq调用
索引,而不是最短的Seq,或者使用
max
明确检查最长的Seq,例如

(0 until (as.length max bs.length)).find(i => as.lift(i) != bs.lift(i))

仅供参考:你应该写
!a、 等于(b)
as
a!=b
旁白:我认为您不需要
大小写=>false
,因为
zip
会截断两个输入中较长的一个(如果长度不同),所以您的第一个大小写总是匹配的?这里有一些关于大小不匹配的压缩列表的解决方案: