Scala-查找两个序列不同的第一个位置
Scala附带了niceScala-查找两个序列不同的第一个位置,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) ... 我很喜欢它的简洁 是否已经定义了一种类似的方法,可以向我报告两个序列不同的第一个位置 也
对应的
方法:
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)
asa!=b
旁白:我认为您不需要大小写=>false
,因为zip
会截断两个输入中较长的一个(如果长度不同),所以您的第一个大小写总是匹配的?这里有一些关于大小不匹配的压缩列表的解决方案: