在Scala中以功能方式比较两个版本?

时间:2019-03-19 16:44:11

标签: scala

Scala中是否有一种惯用的,实用的方式来比较两个虚线版本的字符串,它们的长度可能不同?

例如:

1.0 == 1.0.0
1.2.4 > 1.2
1.10 > 1.2

Java solutions通常是命令式样式)。

5 个答案:

答案 0 :(得分:7)

假设已经获得了版本字符串,因此它们仅包含以点分隔的整数(即“ 1.2.3”,而不是“ 1.2.3-foo-beta-1234-etc”),这可以通过拆分“来完成。 ”,将两个数字序列压缩在一起(使用zipAll对最低有效位置进行零填充),然后依次递归处理该对。

一旦我们发现一个数字与另一个字符串中的数字不同,我们就会得到答案;但是如果数字相同,我们会进一步寻找。

  def versionComp(a: String, b: String) = {
    def nums(s: String) = s.split("\\.").map(_.toInt) 
    val pairs = nums(a).zipAll(nums(b), 0, 0).toList
    def go(ps: List[(Int, Int)]): Int = ps match {
      case Nil => 0
      case (a, b) :: t => 
        if (a > b) 1 else if (a < b) -1 else go(t)
    }
    go(pairs)
  }

示例:

  versionComp("2", "1.1")                     //>  1
  versionComp("1.2", "1.1")                   //>  1
  versionComp("1.2", "1.1.5")                 //>  1
  versionComp("1.2.0", "1.1")                 //>  1
  versionComp("1.2.0.1", "1.2.0.0")           //>  1

  versionComp("1.2.3", "1.2.3")               //>  0
  versionComp("1.2.0", "1.2")                 //>  0
  versionComp("1.2.0.0", "1.2")               //>  0

  versionComp("1.2", "1.5")                   //>  -1
  versionComp("1.2.0", "1.20")                //>  -1
  versionComp("2.20.345", "3.1")              //>  -1

答案 1 :(得分:7)

Kinda一样,但没有递归:

 version1.split("\\.")
   .zipAll(version2.split("\\."), "0", "0")
   .find {case(a, b) => a != b }
   .fold(0) { case (a, b) => a.toInt - b.toInt }

另外,FWIW,我认为这是重复的,因为链接的问题中可接受的答案也很好地回答了这个问题。

答案 2 :(得分:2)

好吧,如果您准备编写一些库代码,则可以创建一个Version 类型,并在其上使用Ordering type类它。

class Version(major: Int, minor: Int, patchOpt: Option[Int], buildOpt: Option[Int], suffixOpt: Option[String])

具有解析器和伴侣对象

中的 Ordering
object Version {
  // NOTE: This here may well not be complete. If this is what you are lokking for I'll update with a tested version
  // Also NOTE: This would be the place where you customize the ordering to your requirements
  implicit val ordering: Ordering[Version] = Ordering.tuple(v =>(v.major, v.minor, v.patchOpt, v.buildOpt, v.suffixOpt))
  def fromString(in: String): Option[Version] = {
    // ... extract all the parts into the proper fields of version or return None
    ???
  }
  def apply(in String): Version = fromVersion(in).getOrElse(throw new Exception())

  def unapply(...):...
}

鉴于此,您可以像这样使用它

// This import enabels infix operations like '<, >, <=' on types 
// for which an Ordering type class exists
import Ordering.Implicits.infixOrderingOps

val v1 = Version("1.22.3")
val v2 = Version("0.33.")
val v3 = Version("1.3.5.6")

v1 < v2 // false
v2 <= v3 // true
... 

对于您的用例来说,这可能是过高的选择,但它为您提供了一种更具表达力的方式来描述您期望的版本以及订购方式。

答案 3 :(得分:2)

您可以使用

BEGIN
    UPDATE useraccounting SET tmpQuta = New.acctoutputoctets
       WHERE EXISTS (SELECT * FROM users WHERE users.userable_id = useraccounting.client_id AND users.userable_type = 'App\\Models\\Client' AND (users.username = New.username));
END

您可能不希望使其隐式,因为它也将代替常规的BEGIN if (NEW.acctoutputoctets != OLD.acctoutputoctets) THEN UPDATE useraccounting SET tmpQuta = tmpQuta + (New.acctoutputoctets - OLD.acctoutputoctets) WHERE EXISTS (SELECT * FROM users WHERE users.userable_id = useraccounting.client_id AND users.userable_type = 'App\\Models\\Client' AND (users.username = New.username)); END IF; END 使用!解决方法是使用值类:

val versionOrdering = 
  Ordering.by { (_: String).split("""\.""").map(_.toInt).toIterable }

def versionComp(a: String, b: String) = versionOrdering.compare(a, b)

是Sascha Kolberg回答的轻量级选择。它可以让您编写例如

String

对于一次性比较,您可以使用

case class Version(asString: String) extends AnyVal

object Version {
  implicit val versionOrdering: Ordering[Version] = 
    Ordering.by { _.asString.split("""\.""").map(_.toInt).toIterable }
}

答案 4 :(得分:0)

另一种方式

val (v1Parts, v2PArts) = (v1.split('.'), v2.split('.'))
v1Parts.zip(v2PArts).map { case (a, b) => a.toInt.compareTo(b.toInt) }
       .dropWhile(_ == 0).headOption.getOrElse(v1Parts.length.compareTo(v2PArts.length))