Comparar números de versión


26

Cuando publicamos algún software, le asignamos un número de versión. Y los usuarios pueden querer actualizar a la última versión de algún software. Entonces, es el momento de averiguar qué versión debería ser más nueva.

Entrada

Ingrese dos números de versión como cadenas.

En el contexto de este desafío, solo admitimos números de versión que son algunos dígitos unidos por puntos.

  • Un número de versión es una cadena que solo puede contener dígitos ( 0~ 9) y puntos ( .).
  • Los puntos no serían el primer / último carácter de un número de versión.
  • Debe haber algunos dígitos entre puntos. No pueden aparecer dos puntos continuamente.
  • Todos los números en un número de versión serían menores que 2 16 .

Salida

Compare los números de versión ingresados ​​y genere si el primero es mayor que / igual a / menor que el segundo. Puede elegir una de las siguientes presentaciones:

  • Use número positivo / cero / número negativo, mientras que cero significa igual;
  • Use tres valores distintos constantes;

Comparando

No es necesario que implemente el algoritmo descrito en esta sección. Su envío es válido siempre que resulte el mismo resultado con este algoritmo.

  • Los números de versión son algunos números decimales unidos por puntos. Primero dividimos los dos números de versión en matrices de números;
  • Rellenar el final de las matrices con ceros para que tengan la misma longitud;
  • Compare desde el primer elemento hasta el último:
    • Si los dos elementos de la matriz son diferentes, el número mayor significa un número de versión mayor
    • Si son iguales, continúe comparando los siguientes elementos;
    • Si todos los elementos de la matriz son iguales, las dos versiones son iguales.

Casos de prueba

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 tiene un objeto Versión, pero no se admite un solo carácter :(
Brian J

@BrianJ y agregar '.0' cuesta a muchos personajes? :)
RobAu

Bueno, en realidad espera 2, 3 o 4 porciones. Por lo tanto, falla en el caso de prueba 1.0.0.1.0 (aunque intenté su idea inicialmente :))
Brian J

Creo que Windows tiene una función integrada que hará esto: StrCmpLogicalW
bace1000

Respuestas:



6

05AB1E (heredado) , 15 14 13 bytes

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

Salidas -1 [] 1para < = >respectivamente.

-1 byte gracias a @Emigna .

Pruébelo en línea o verifique todos los casos de prueba .

Explicación:

'.¡              # 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
Puedes usar en 0Klugar de ʒĀ}.
Emigna

@Emigna Ah, por supuesto ... Gracias.
Kevin Cruijssen

5

R , 32 bytes

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

Pruébalo en línea!

Usando un R incorporado

Salidas 1 2, 1.5 1.5, 2 1por menos, igual, mayor.


Mejor hasta ahora, sin construir:

R , 151 142 125 107 bytes

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

Pruébalo en línea!

Código desenrollado con explicación:

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)

Salidas -1, NULL, 1por menos, igual, mayor.


Concepto original, golfizado usando sapply, [<-y %*%:

R , 129 bytes

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]

Pruébalo en línea!

Ahora tiene una lista de dos vectores de enteros de igual longitud. Calcule las diferencias por pares usando Reducey genere el primer elemento que no sea cero usando la pequeña y delicada w[!!w][1]forma al final.

Salidas -1, NA, 1por menos, igual, mayor.


¡Impresionante! Golf rápido: nueva línea adicional al final de su código; debe tener 150 bytes;)
JayCe

reducir el número de variables con nombre ... . Siento que hay una manera de hacerlo usando una matriz en lugar de listas, pero aún no he encontrado cómo hacerlo.
JayCe

1
Puede reducir esto a 100 bytes usando 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](o 106 si desea devolver -1, NA, 1 no (negativo), NA, (positivo).
mnel

1
@mnel la solución de 100 bytes necesita un poco de trabajo. Falla en los últimos dos casos de prueba. El relleno tiene que ser 0y no (implícitamente) NA. He respondido un Wiki de la comunidad para que cualquiera que pueda solucionarlo pueda agregarlo.
ngm

1
@digEmAll jugó 4 bytes al calcular primero el signo y luego hacer Find(c,x). Creo que es un nuevo truco.
JayCe

4

APL (Dyalog Unicode) , 18 17 bytes

1 byte guardado gracias a @ Adám por usar en ⍤1lugar de ∘↑(...)¨y cambiando el formato de entrada de una matriz anidada a una matriz

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

Pruébalo en línea!

Toma la entrada como una matriz de caracteres como el argumento correcto, donde cada cadena de versión está en su propia fila. Salidas ¯1 1, 0 0, 1 ¯1para <, =, >respectivamente.

(⍎¨∊∘⎕D⊆⊢)⍤1 en cada fila

  • ∊∘⎕D⊆⊢ agrupar todas las apariciones de dígitos, es decir, dividir en .

  • ⍎¨ y convertir cada una de estas ocurrencias en un número

convertir a una matriz, donde la primera entrada está en la fila superior y la segunda en la parte inferior, rellenando con 0s donde sea necesario

(⍋-⍒) y

  • - sustraer
    • los índices en las filas que los ordenarían en orden descendente
    • igual que la parte superior pero para orden ascendente

4

Perl 6 , 63 47 22 bytes

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

Pruébalo en línea!

Resulta que Perl 6 tiene un tipo de versión que se ajusta bastante a la descripción. Este es un bloque de código anónimo que toma una lista de dos cadenas de versión y devuelve More, Sameo Less.

Explicación:

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

O, sin tipos incorporados para 47 bytes:

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

Pruébalo en línea!

Bloque de código anónimo que toma dos cadenas y devuelve Moresi el segundo es mayor, Lesssi el segundo es más pequeño y Nilsi son iguales.

Explicación:

{                                             } # 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

Brachylog , 49 40 bytes

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

... Sigue siendo bastante impresionante.

Espera una lista de dos cadenas. Usos positive number / zero / negative number como > / = / <.

Pruébalo en línea!

Explicación

Dividiendo las entradas

Dada una entrada que no unificar con [0, 0], tales como ["1.02.0", "1.2.0.1.0"], el siguiente salidas de segmento, por ejemplo, [[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]

Comparar las entradas

Dado, por ejemplo, [[1, "02.0"], [1, "2.0.1.0"]]comprime las sublistas [[1, 1], ["02.0", "2.0.1.0"]]y compara los valores en el encabezado ( [1,1]). Recurre en la segunda sublista. Tenga en cuenta que el predicado zip zrecorre las listas más cortas, de modo que comprimir con [0,0]es equivalente a comprimir con [0], por lo tanto, el paso anterior se unifica 0con 0sin agregar más valores.

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 bytes

Guardado 5 bytes gracias a @redundancy

(a)(b)0

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

Pruébalo en línea!


Agradable. Si he entendido correctamente, puede guardar bytes sustituyendo replacecon fill. Los operandos para -se intercambian ya que ambos deben ser obligados a un número. Pruébalo en línea!
redundancia

@redundancy ¡Buena idea! (Sin embargo, no estoy seguro si mi implementación es exactamente lo que tenía en mente.)
Arnauld

Supuse que su intención era agregar suficientes valores coercibles a 0, de modo que el mapeo sobre las subcadenas de aeventualmente recorra esos valores 0 si bcontiene más segmentos de números que a. Sucede que el método más corto para garantizar que sea así es dividirlo en una bcadena de longitud de '.' aprovechando la división existente aplicada a a.
redundancia

3

Java (JDK 10) , 201 96 89 bytes

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

Pruébalo en línea!

Devuelve un número negativo si la primera versión es más pequeña que la segunda, una positiva si la primera versión es mayor que la segunda y 0si son iguales.

Sí, eso es un trabajo pesado para "simplemente" llamar a un incorporado!

Créditos


1
Lo intenté, pero solo puedo eliminar tres bytes. 228 bytes
Kevin Cruijssen

1
Encontramos algo más: 217 bytes
Kevin Cruijssen

1
Probablemente sea eso. Ya lo intenté try-finallypara que el if-check pueda simplificarse; intentado volver dentro del bucle if t!=0; intentado usar Integery i.compare(i.valueOf(...),i.valueOf(...)); intenté usar genéricos como este <T>T[]g(T s){return(T[])(s+"").replaceAll("(\\.0+)*$","").split("\\.");}; etc. Todos son 2-6 bytes más largos. Si usted (o alguien más) encuentra algo más, hágamelo saber por favor. Curioso por saber qué. :)
Kevin Cruijssen

1
@KevinCruijssen No, no puedo porque "Todos los números en un número de versión serían menores que 2^16". Rangos cortos de - (2 ^ 15) a 2 ^ 15-1.
Olivier Grégoire

1
@KevinCruijssen ¡Podría eliminar 105 bytes! ¿Cómo? Bueno, encontré un incorporado;)
Olivier Grégoire


2

Retina 0.8.2 , 54 bytes

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

Pruébalo en línea! El enlace incluye casos de prueba. Utiliza el valor del separador como salida de igualdad, por lo que, por conveniencia, el encabezado convierte el separador de entrada, =pero podría ser cualquier cosa que no esté dentro [.\d]. Explicación:

\d+
$*

Convierte a unario.

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

Elimina repetidamente el primer carácter de cada lado hasta que difieran o un lado se agote. Esto es mucho más rápido que intentar unir prefijos, aunque posiblemente no sea más golfista. En este punto, las cadenas están en una de varias formas, que deben decodificarse para obtener un resultado de comparación.

  1. Si ninguna cadena contiene un, 1entonces el resultado es=
  2. Si la cadena izquierda comienza con a, 1entonces el resultado es>
  3. Si la cadena correcta comienza con a, 1entonces el resultado es<
  4. Si la cadena izquierda está vacía, el resultado es <
  5. En este punto, la cadena derecha está vacía, por lo que el resultado es >

Otra forma de pensar en esto es que si una cadena contiene a 1y la otra no comienza con a, 1entonces esa cadena es mayor, sin embargo, eso resulta ser un byte más largo.

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

Verifique el caso 3 o el caso 4 sin el caso 1.

.*1.*=.*
>

Si la cadena izquierda todavía contiene un a 1en este punto, entonces es mayor.

\.

De lo contrario, elimine cualquier sobrante de .s.

Firefox Browser Console REPL, 19 bytes

Services.vc.compare

Creo que esta función interna realiza la comparación requerida. Devuelve -1, 0 o 1.


1
Te sugiero que publiques el código Chrome de Firefox como otra respuesta ...
tsh

por cierto, no estoy seguro de cómo el código de Chrome de Firefox cuenta sus bytes. ¿Debería Cu.import("resource://gre/modules/Services.jsm");contarse?
tsh

1
@tsh Por eso agregué "REPL de la consola del navegador" ...
Neil


2

C (gcc) ,  140  134 bytes

Este código genera un negativo, 0o un positivo para <, =o >respectivamente.

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;}

Pruébalo en línea!

Ediciones:

  • ¡Guardado 6 bytes gracias a ceilingcat!

El desafío dice: "Use tres valores distintos constantes"; Su código no devuelve constantes.
Olivier Grégoire

1
@Olivier Establece que puedo "Usar tres valores distintos constantes"; O "Use número positivo / cero / número negativo, mientras que cero significa igual";
Annyo

¡Mi error! Estás en lo correcto.
Olivier Grégoire


1

JavaScript (Node.js) , 105 88 80 bytes

-17 bytes de @redundancy. ¡Guauu!

-8 bytes eliminando Math.sign. Gracias @tsh

Devuelve un valor negativo, cero o positivo.

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

Pruébalo en línea!


1
88 bytes utilizando execpara dividir cadenas. Pruébalo en línea!
redundancia

@redundancy Maldición, gracias! eso es un truco bastante fresco
Luis Felipe de Jesús Muñoz

Tal vez desee eliminar Math.sign para guardar algunos bytes cambiando a valores positivos / cero / negativos. Y tal vez se requiera un signo positivo.
tsh


0

Limpio , 116 111 bytes

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)

Pruébalo en línea!

Emite un número negativo cuando el primer argumento es menor que el segundo, cero cuando son equivalentes y un número positivo cuando es mayor que el segundo.


0

Swift 4 , 155 bytes

Encabezado (sin contar: el código no es recursivo):

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

Código

{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)}

Pruébalo en línea!

Explicaciones

  • Recortamos el final .0.
  • Comparamos componentes numéricamente.

Constantes devueltas

  • nulo para =
  • cierto para <
  • falso para>

0

JavaScript 64 bytes

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

Pruébalo en línea!

Con comentarios:

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

Burlesque - 17 bytes

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

Si desea salida en '> <=', agregue ?i"<=>"j!!Q.


0

Powershell, 88 bytes

Devuelve 0para igual, a positive integerpara mayor que o a negative integerpara menor que.

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

Menos guión de prueba de golf:

$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"
}

Salida:

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

Dart , 277 231 bytes

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);}

Pruébalo en línea!

  • -44 bytes usando variables para almacenar longitud y usando ternary en bucle
  • -2 bytes eliminando los paréntesis for

0

Swift 4 + Foundation , 160 bytes (142 + 18) , 155 bytes (142 + 13)

Importar (13 bytes, incluso ;para separar del código):

Esto importará Foundation, pero es 5 bytes más corto que import Foundation.

import UIKit;

Encabezado (sin contar: el código no es recursivo):

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

Código (142 bytes):

{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)}

Pruébalo en línea!

Explicaciones

  1. Agregamos algunos .0 finales para el mismo número de componentes.
  2. Comparamos componentes numéricamente.

Constantes devueltas

  • ComparisonResult.orderedSame for =
  • ComparisonResult.orderedAscending para <
  • ComparisonResult.orderedDescending para>

No estoy seguro si contamos la importdeclaración, por lo que publiqué una respuesta separada que no requiere Foundationy con un recuento de bytes entre 142 bytes (sin contar la importación) y 160 bytes (contando la importación).
Coeur

0

Zsh , 54 bytes

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

Pruébalo en línea! ¡Prueba una suite de prueba!

Esto evales para las siguientes ocho declaraciones:

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
<<<$?

Entonces los tres valores únicos son:

 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>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.