比较版本号


26

发布某些软件时,我们会为其分配一个版本号。用户可能需要更新到某些软件的最新版本。因此,现在是时候找出哪个版本应该较新。

输入项

输入两个版本号作为字符串。

在此挑战的背景下,我们仅支持版本号,该版本号是一些由点组成的数字。

  • 版本号是一个字符串,只能包含数字(09)和点(.)。
  • 点号不是版本号的第一个/最后一个字符。
  • 点之间必须有一些数字。不能连续出现两个点。
  • 版本号中的所有数字都应小于2 16

输出量

比较输入的版本号并输出是否第一个大于/等于/小于第二个。您可以选择以下演示文稿之一:

  • 使用正数/零/负数,而零表示相等;
  • 使用三个恒定的不同值;

比较中

您无需实施本节中描述的算法。您的提交是有效的,只要它与该算法产生相同的输出即可。

  • 版本号是一些小数点,由点连接。我们首先将两个版本号拆分为数字数组;
  • 用零填充数组的末尾以使它们具有相同的长度;
  • 比较第一项和最后一项:
    • 如果两个数组项不同,则数字越大表示版本号越大
    • 如果相同,则继续比较以下项目;
    • 如果数组中的所有项目都相等,则两个版本相等。

测试用例

version1  version2  result
2         1         >
1.0.0     1         =
1.0       1.0.0     =
1.2.42    1.2.41    >
1.1.56789 1.2.0     <
1.10      1.2       >
1.20      1.150     <
18.04     18.4      =
7.010     7.8       >
1.0.0.1.0 1.00.00.2 <
00.00.01  0.0.0.1   >
0.0.1     0.1       <
42.0      4.2.0     >
999.999   999.999.1 <
2018.08.1 2018.08   >


.NET具有一个Version对象,但其中不支持单个字符:(
Brian J

@BrianJ并将'.0'附加到许多字符上?:)
RobAu

好吧,它实际上期望2、3或4部分。因此它在1.0.0.1.0测试用例上失败了(尽管我最初确实尝试过您的想法:))
Brian J,

我认为Windows具有内置的功能,可以执行以下操作:StrCmpLogicalW
bace1000'bace1000'18

Answers:



6

05AB1E(传统)15 14 13 字节

'.¡0ζε`.S}0K¬

输出-1 [] 1< = >分别。

-1个字节感谢@Emigna

在线尝试验证所有测试用例

说明:

'.¡              # Split on dots
                 #  i.e. ['1.0.1.1.0','1.00.2.0']
                 #   → [['1','0','1','1','0'],['1','00','2','0']]
   0ζ            # Zip, swapping rows and columns, using '0' as filler
                 #  i.e. [['1','0','1','1','0'],['1','00','2','0']]
                 #   → [['1','1'],['0','00'],['1','2'],['1','0'],['0','0']]
     ε   }       # Map each:
      `          #  Push both values to the stack
       .S        #  And calculate the signum (1 if a>b; -1 if a<b; 0 if a==b)
                 #   i.e. [['1','1'],['0','00'],['1','2'],['1','0'],['0','0']]
                 #    → [0,0,-1,1,0]
          0K     # Remove all zeros
                 #  i.e. [0,0,-1,1,0] → [-1,1]
            ¬    # Then take the head as result
                 #  i.e. [-1,1] → -1

1
您可以使用0K代替ʒĀ}
Emigna

@Emigna当然是啊..谢谢。
凯文·克鲁伊森

5

R,32个字节

rank(numeric_version(scan(,"")))

在线尝试!

使用内置的R

输出1 21.5 1.52 1少,等于,大于。


到目前为止最好的,没有内置的:

R151142125107字节

function(v,L=strsplit(v,'\\.'))Find(c,sign(Reduce('-',Map(as.double,Map(c,L,Map(rep,0,rev(lengths(L))))))))

在线尝试!

展开代码并提供说明:

function(v){             # character vector of 2 elements as function arg;
  L=strsplit(v,'\\.')    # obtain a list of two character vectors
                         # with the separated version numbers;
  R=rev(lengths(L))      # store in vector R the lengths of the 2 vectors and reverse it;
  M1=Map(rep,0,R)        # create a list of 2 vector containing zeros
                         # repeated R[1] and R[2] times;
  M2=Map(c,L,M1)         # append to the vectors in list L the zeros in M1;
  M3=Map(as.double,M2)   # convert the character vectors in M2 to double;
  w=sign(Reduce('-',M3)  # compute the sign of element by element difference M[[1]] - M[[2]]);
  Find(c,w)            # returns the first non zero element in w, if none return NULL;
}
# N.B. as.double is necessary because "0XX" is interpreted as octal by strtoi unless 
#      we use strtoi(x,10) which is exactly the same length of as.double(x)

输出-1NULL1少,等于,大于。


最初的概念,golfed下使用sapply[<-%*%

R,129字节

function(x,y=strsplit(x,"\\."),w=sign(sapply(y,function(x)strtoi("[<-"(rep(0,max(lengths(y))),seq(x),x),10))%*%c(1,-1)))w[!!w][1]

在线尝试!

现在,您有了两个等长整数向量的列表。使用来计算成对差异,最后Reduce使用棘手的小w[!!w][1]形式输出第一个非零元素。

输出-1NA1少,等于,大于。


令人印象深刻!快速高尔夫:代码末尾的额外换行符-它应该为150个字节;)
JayCe

降低命名变量的数量...。我觉得有一种方法可以使用矩阵来代替列表,但是我还没有找到方法。
JayCe

1
您可以使用scan function(a,b,d=scan(t=a,se='.'),e=scan(t=b,se='.'),f=1:max(lengths(list(d,e))),g=d[f]-e[f])g[!!g][1](或如果您想传回-1,NA,1不是(负数),NA,(正数),则减少至100个位元组。)
mnel 18'Aug

1
@mnel 100字节解决方案需要一些工作。在最后两个测试用例中失败。填充必须是0(不是)(不是)NA。我已经将答案设为Community Wiki,因此只要可以解决问题的人都可以添加它。
ngm

1
@digEmAll首先计算符号,然后做4个字节Find(c,x)。我认为这是一个新技巧。
JayCe

4

APL(Dyalog Unicode)18 17字节

@Adám可以⍤1代替使用,∘↑(...)¨也可以通过将输入格式从嵌套数组更改为矩阵来节省1个字节

(⍋-⍒)(⍎¨∊∘⎕D⊆⊢)⍤1

在线尝试!

将输入作为字符矩阵作为正确的参数,其中每个版本字符串在其自己的行上。输出¯1 10 01 ¯1<=>分别。

(⍎¨∊∘⎕D⊆⊢)⍤1 在每一行

  • ∊∘⎕D⊆⊢ 将所有出现的数字分组,即分割 .

  • ⍎¨ 并将这些事件中的每一个转换为一个数字

转换为矩阵,其中第一个输入在最上面一行,第二个输入在最下面一行0,必要时用s 填充

(⍋-⍒)

  • - 减去
    • 索引到行中,将按降序对其进行排序
    • 与顶部相同,但升序

4

Perl 6的63个47 22字节

{"v$^a cmp v$^b".EVAL}

在线尝试!

事实证明,Perl 6的版本类型非常符合说明。这是一个匿名代码块,它有两个版本字符串并返回一个列表或者MoreSame或者Less

说明:

{                    }  # Anonymous code block
 "             "        # Create a string of code
  v$^a cmp v$^b         # Comparing the two versions
                .EVAL   # And EVAL it

或者,没有47个字节的内置类型:

{first +*,[Z<=>] map *.split('.')[^@_.ords],@_}

在线尝试!

匿名代码块,它接收两个字符串,More如果第二个字符串较大,第二个字符串Less较小且Nil相等,则返回。

说明:

{                                             } # Anonymous code block
                 map *.split('.')          ,@_  # Split both strings by '.'
                                 [^@_.ords]     # Pad the lists by a lot
          [Z<=>]   # Zip the strings with the <=> operator
 first +*,  # Get the first value that when coerced to an int, is not 0

3

Brachylog49 40字节

+0|{~c[H,".",T]hị;T|ị;0|0}ᵐz{h-0&t↰₀|h-}

...它的长度仍然很短。

需要两个字符串的列表。用途positive number / zero / negative number> / = / <

在线尝试!

说明

分割输入

给定与输入不统一的输入[0, 0],例如["1.02.0", "1.2.0.1.0"]以下分段输出,例如[[1, "02.0"], [1, "2.0.1.0"]]

                            # unify the input with...
+0                          # : a list whose sum = 0 (output is 0)
  |{                     }ᵐ # : OR a list that when mapped...
    ~c                      # : : if the input string unifies with a list of the form...
      [H,".",T]             # : : : e.g. "1.02.0", H = "1", T = "02.0"
               hị           # : : : coerce the head to an integer
                 ;T         # : : : append the string T
                            # : : : "1.02.0" -> [1, "02.0"]
                   |ị       # : : OR it unifies with an integer
                     ;0     # : : : append 0
                            # : : : "1" -> [1, 0]
                       |0   # : : OR it unifies with 0
                            # : : : 0 -> [0]

比较输入

给定(例如)[[1, "02.0"], [1, "2.0.1.0"]],将子列表压缩到其中[[1, 1], ["02.0", "2.0.1.0"]]并比较head([1,1])中的值。重复第二个子列表。请注意,zip谓词z在较短的列表中循环,因此with 的zip [0,0]等效于zip的zip [0],因此,上一步00没有附加附加值的情况下是统一的。

z             # zip the sublists
 {          } # unify the result (r) with...
  h           # : take the head of the result
   -          # : : subtract the second value from the first
    0         # : : if the difference unifies with 0...
     &t↰₀     # : : recur on the tail of r
         |h-  # : OR unify with the difference of the elements of the head
              # : (equivalent to returning early)

3

JavaScript(ES6),73 68字节

@redundancy节省了5个字节

将输入作为(a)(b)。返回为相等,对于正整数大于或为一个负整数小于0

a=>b=>(a+[...b].fill`.`).split`.`.some((x,i)=>d=~b.split`.`[i]-~x)*d

在线尝试!


真好 如果我理解正确的话,您可以通过替换保存字节replacefill-交换操作数,因为现在必须将两者都强制转换为数字。在线尝试!
冗余

@redundancy好主意!(不过,不确定我的实现是否正是您的初衷。)
Arnauld

我以为您的意图是将足够多的值强制地附加到0,以便a如果b包含的数字段比个多,则映射到最终循环通过这些0值的子字符串a。碰巧的是,确保是的最短方法是将b-length字符串分割为'。'。通过利用应用于的现有拆分a
冗余

3

Java(JDK 10)201 96 89字节

java.util.Comparator.comparing(java.lang.module.ModuleDescriptor.Version::parse)::compare

在线尝试!

如果第一个版本小于第二个版本,则返回负数;如果第一个版本大于第二个版本且0相等,则返回正数。

是的,“仅”调用一个内置函数是一项繁重的工作!

学分


1
我尝试过,但是我只能删除三个字节
。228

1
发现更多内容:217个字节
Kevin Cruijssen,

1
可能就是这样。已经尝试过,try-finally因此可以简化if-check的操作;尝试如果循环内返回t!=0; 尝试使用Integeri.compare(i.valueOf(...),i.valueOf(...)); 尝试使用这样的泛型<T>T[]g(T s){return(T[])(s+"").replaceAll("(\\.0+)*$","").split("\\.");}; 等等。全都长2-6个字节。如果您(或其他任何人)确实找到更多东西,请告诉我。好奇地知道什么。:)
Kevin Cruijssen

1
@KevinCruijssen不,我不能因为“版本号中的所有数字都小于2^16”。短范围是-(2 ^ 15)到2 ^ 15-1。
奥利维尔·格雷戈尔(OlivierGrégoire),

1
@KevinCruijssen我可以删除105个字节!怎么样?好吧,我找到了一个内置的;)
OlivierGrégoire18年


2

视网膜0.8.2,54字节

\d+
$*
+`^(.)(.*=)\1
$2
(.*=|^=.*)1.*
<
.*1.*=.*
>
\.

在线尝试!链接包括测试用例。将分隔符值用作相等输出,因此,为方便起见,标头将输入分隔符转换为,=但可能不是in中的任何内容[.\d]。说明:

\d+
$*

转换为一元。

+`^(.)(.*=)\1
$2

从每一侧重复删除第一个字符,直到它们不同或一侧用完为止。这比尝试匹配前缀要快得多,尽管可能不适合高尔夫球手。此时,字符串采用几种形式之一,需要将其解码以得到比较结果。

  1. 如果两个字符串都不包含,1则结果为=
  2. 如果左字符串以a开头,1则结果为>
  3. 如果正确的字符串以a开头,1则结果为<
  4. 如果左字符串为空,则结果为 <
  5. 此时,正确的字符串为空,因此结果为 >

对此的另一种思考方式是,如果一个字符串包含a 1,而另一个不以a开头,1则该字符串更大,但是结果却更长了一个字节。

(.*=|^=.*)1.*
<

检查情况3,或检查情况4,而不检查情况1。

.*1.*=.*
>

如果此时左字符串仍包含a 1,则该字符串更大。

\.

否则,删除任何剩余的.s。

Firefox浏览器控制台REPL,19字节

Services.vc.compare

我相信此内部功能可以执行所需的比较。它返回-1、0或1。


1
我建议您将Firefox chrome代码发布为另一个答案……
tsh

顺便说一句,我不确定Firefox chrome代码如何计算其字节数。应该Cu.import("resource://gre/modules/Services.jsm");算吗?
tsh

1
@tsh这就是为什么我添加“浏览器控制台REPL” ...的原因
Neil


2

C(GCC)  140个  134字节

该代码输出负,0或正对<=>分别。

i;n;p;q;g(char*s){for(i=n=0;*s&&++n&&*s-46;i=i*10+*s++-48);i=i;}f(char*a,char*b){for(p=q=0;*a+*b&&p==q;b+=n)p=g(a),a+=n,q=g(b);a=p-q;}

在线尝试!

编辑:

  • 多亏了ceilingcat,节省了6个字节!

挑战指出:“使用三个恒定的不同值;” 您的代码不返回常量。
奥利维尔·格雷戈雷

1
@Olivier它声明我可以“使用三个恒定的不同值;” 或“使用正数/零/负数,而零表示相等;”
安约



1

JavaScript(Node.js)105 88 80字节

@redundancy中的-17个字节。哇!

-8个字节,删除Math.sign。谢谢@tsh

返回负,零或正值

f=(a,b,r=/(\d*).?(.*)/)=>a+b&&+((a=r.exec(a))[1]-(b=r.exec(b))[1]||f(a[2],b[2]))

在线尝试!


1
88个字节exec用于拆分字符串。在线尝试!
冗余

@redundancy该死,谢谢!多数民众赞成在一个很酷的把戏
路易斯·费利佩·德·耶稣·穆诺兹

也许您想删除Math.sign来通过切换为正/零/负值来节省一些字节。也许需要一个积极的信号。
tsh


0

干净116个 111字节

import StdEnv,Text
?s=map toInt(split"."s)
$a b= @(?a)(?b)
@[h:t][u:v]|h==u= @t v=h-u
@l[]=sum l
@[]l= ~(sum l)

在线尝试!

当第一个参数小于第二个参数时输出一个负数,当它们相等时输出零,而当它大于第二个参数时输出一个正数。


0

Swift 4,155个字节

标头(不计:代码是非递归的):

let f:(String,String)->Bool? = 

{let x:(String)->[Int]={$0.split{$0=="."}.map{Int($0)!}.reversed().drop{$0==0}.reversed()},a=x($0),b=x($1)
return a==b ?nil:a.lexicographicallyPrecedes(b)}

在线尝试!

说明

  • 我们修剪尾随.0。
  • 我们在数字上比较组件。

返回常数

  • 零=
  • 对于<
  • 虚假>

0

JavaScript 64字节

a=>b=>(e=i=>(g=v=>v.split`.`[i]||0)(a)-g(b)||!a[i]-1&&e(i+1))(0)

在线尝试!

有评论:

a=>b=>(                            // Main function takes arguments like ("1.2.42")("1.2.41")
    e=i=>                          // e(i) compares the ith number, returns >0, <0 or =0.
        (   g=v=>v.split`.`[i]||0  // g() returns the ith string or 0
        )(a)                       // call g(a)
        -g(b)                      // subtracting g(b) from g(a) casts strings to integer
        ||                         // If they are not equal return result now
        !a[i]-1 &&                 // recursion limited to a.length, always sufficient
        e(i+1)                     // next i
    )(0)                           // Start with i = 0


0

滑稽-17字节

wd{'.;;)ri}m[^pcm


blsq ) "2018.08.1 2018.08"wd{'.;;)ri}m[^pcm
1
blsq ) "0.0.1 0.1"wd{'.;;)ri}m[^pcm
-1
blsq ) "1.1.56789 1.2.0"wd{'.;;)ri}m[^pcm
-1

如果要在'> <='中输出,请添加?i"<=>"j!!Q


0

Powershell,88字节

返回0等于,a positive integer大于或negative integer小于。

param($a,$b)+(($x=$a-split'\.')+($y=$b-split'\.')|%{$x[+$i]-$y[$i++]}|?{$_}|Select -f 1)

少打高尔夫的测试脚本:

$f = {

param($a,$b)
$x=$a-split'\.'
$y=$b-split'\.'
$z=$x+$y|%{
    $x[+$i]-$y[$i++]
}|?{$_}|Select -first 1
+$z             # convert $null to 0

}

@(
    ,("2"         ,"1"         , 1)
    ,("1.0.0"     ,"1"         , 0)
    ,("1.0"       ,"1.0.0"     , 0)
    ,("1.2.42"    ,"1.2.41"    , 1)
    ,("1.1.56789" ,"1.2.0"     ,-1)
    ,("1.10"      ,"1.2"       , 1)
    ,("1.20"      ,"1.150"     ,-1)
    ,("18.04"     ,"18.4"      , 0)
    ,("7.010"     ,"7.8"       , 1)
    ,("1.0.0.1.0" ,"1.00.00.2" ,-1)
    ,("00.00.01"  ,"0.0.0.1"   , 1)
    ,("0.0.1"     ,"0.1"       ,-1)
    ,("42.0"      ,"4.2.0"     , 1)
    ,("999.999"   ,"999.999.1" ,-1)
    ,("2018.08.1" ,"2018.08"   , 1)
) | % {
    $v1,$v2,$expected = $_
    $result = &$f $v1 $v2
    "$([Math]::Sign($result)-eq$expected): $result"
}

输出:

True: 1
True: 0
True: 0
True: 1
True: -1
True: 8
True: -130
True: 0
True: 2
True: -1
True: 1
True: -1
True: 38
True: -1
True: 1

0

飞镖277231字节

F(s,{t}){t=s.split('.').map(int.parse).toList();while(t.last<1)t.removeLast();return t;}f(a,b,{d,e,f,g,h,i=0}){d=F(b);e=F(a);g=d.length;h=e.length;f=h>g?g:h;for(;i<f;i++)if(e[i]!=d[i])return e[i]>d[i]?1:-1;return h>g?1:(h<g?-1:0);}

在线尝试!

  • -44字节,使用变量存储长度并在循环中使用三进制
  • 通过删除for括号-2个字节

0

Swift 4 + Foundation160字节(142 + 18),155字节(142 + 13)

导入(13个字节,包括;与代码分开的部分):

这将导入Foundation,但比短5个字节import Foundation

import UIKit;

标头(不计:代码是非递归的):

let f:(String,String)->ComparisonResult =

代码(142字节):

{var x={($0 as String).split{$0=="."}.count},a=$0,b=$1
while x(a)<x(b){a+=".0"}
while x(b)<x(a){b+=".0"}
return a.compare(b,options:.numeric)}

在线尝试!

说明

  1. 对于相同数量的组件,我们在其末尾附加.0。
  2. 我们在数字上比较组件。

返回常数

  • CompareResult.orderedSame for =
  • <的CompareResult.orderedAscending
  • >的CompareResult.orderedDescending

我不确定是否要计算该import语句,因此我发布了一个不需要的单独答案Foundation其字节数介于142字节(不计算导入)和160字节(对导入计数)之间。
心教堂

0

Zsh,54个字节

eval {autoload,}' is-at-least $'{1\ $2,2\ $1}';<<<$?;'

在线尝试! 尝试测试套件!

eval是以下八个语句:

autoload is-at-least $1 $2     # loads the "is-at-least" function
<<<$?                          # success, prints 0
autoload is-at-least $2 $1     # redundant
<<<$?                          # success, prints 0
is-at-least $1 $2              # exits 1 if $1 < $2
<<<$?
is-at-least $2 $1              # exits 1 if $2 < $1
<<<$?

因此,三个唯一值是:

 cmp |  value
-----+------------------------------------------
  =  |  0<newline>0<newline>0<newline>0<newline>
  <  |  0<newline>0<newline>1<newline>0<newline>
  >  |  0<newline>0<newline>0<newline>1<newline>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.