Compare números de versão


26

Quando publicamos algum software, atribuímos um número de versão a ele. E os usuários podem querer atualizar para a versão mais recente de alguns softwares. Portanto, é hora de descobrir qual versão deve ser mais nova.

Entrada

Insira dois números de versão como seqüências de caracteres.

No contexto desse desafio, suportamos apenas números de versão, que são alguns dígitos unidos por pontos.

  • Um número de versão é uma sequência que pode conter apenas dígitos ( 0~ 9) e pontos ( .).
  • Pontos não seriam o primeiro / último caractere de um número de versão.
  • Deve haver alguns dígitos entre os pontos. Dois pontos não podem aparecer continuamente.
  • Todos os números em um número de versão seriam menores que 2 16 .

Saída

Compare os números de versão introduzidos e produza se o primeiro é maior que / igual a / menor que o segundo. Você pode escolher uma das seguintes apresentações:

  • Use número positivo / zero / número negativo, enquanto zero significa igual;
  • Use três valores distintos constantes;

Comparando

Você não é obrigado a implementar o algoritmo descrito nesta seção. Seu envio é válido desde que resulte a mesma saída com este algoritmo.

  • Os números de versão são alguns números decimais unidos por pontos. Primeiro, dividimos os dois números de versão em matrizes de números;
  • Preenchendo o final de matrizes com zeros para torná-los com o mesmo comprimento;
  • Compare do primeiro item ao último:
    • Se os dois itens da matriz forem diferentes, o número maior significa um número de versão maior
    • Se forem iguais, continue a comparar os seguintes itens;
    • Se todos os itens da matriz forem iguais, as duas versões serão iguais.

Casos de teste

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 tem um objeto Version, mas um único caractere não é suportado nele :(
Brian J

@BrianJ e anexando custos '.0' a muitos caracteres? :)
RobAu

Bem, ele realmente espera 2, 3 ou 4 porções. Então ele falhar no caso 1.0.0.1.0 teste (apesar de eu tentar a sua ideia inicialmente :))
Brian J

Eu acho que o Windows tem um built-in que fará isso: StrCmpLogicalW
bace1000

Respostas:



6

05AB1E (herdado) , 15 14 13 bytes

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

Saídas -1 [] 1para < = >respectivamente.

-1 byte graças a @Emigna .

Experimente online ou verifique todos os casos de teste .

Explicação:

'.¡              # 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
Você pode usar em 0Kvez de ʒĀ}.
Emigna

@ Emigna Ah, é claro .. Obrigado.
Kevin Cruijssen

5

R , 32 bytes

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

Experimente online!

Usando um R embutido

Saídas 1 2, 1.5 1.5, 2 1para menos, igual, maior.


Melhor até agora, sem embutido:

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

Experimente online!

Código desenrolado com explicação:

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)

Saídas -1, NULL, 1para menos, igual, maior.


Conceito original, golfed para baixo usando sapply, [<-e %*%:

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]

Experimente online!

Agora você tem uma lista de dois vetores de comprimento igual de números inteiros. Calcule as diferenças entre pares usando Reducee produza o primeiro elemento diferente de zero usando a pequena w[!!w][1]forma complicada no final.

Saídas -1, NA, 1para menos, igual, maior.


Impressionante! Golf rápida: nova linha extra no final do seu código - deve ser de 150 bytes;)
Jayce

reduzir o número de variáveis nomeadas ... . Eu sinto que há uma maneira de fazê-lo usando uma matriz em vez de listas, mas ainda não encontrei como fazê-lo.
Jayce

1
Você pode obter esse baixo para 100 bytes utilizando 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](ou 106 se você quiser retornar -1, NA, 1 não (negativa), NA, (positivo).
mnel

1
@mnel a solução de 100 bytes precisa de um pouco de trabalho. Falha nos dois últimos casos de teste. O preenchimento deve ser 0e não (implicitamente) NA. Fiz a resposta em um Wiki da comunidade para que quem puder consertá-lo possa adicioná-lo.
ngm

1
@digEmAll jogou 4 bytes calculando primeiro o sinal e depois o faz Find(c,x). Eu acho que é um novo truque.
JayCe 23/08/19

4

APL (Dyalog Unicode) , 18 17 bytes

1 byte salvo graças a @ ADAM para usar ⍤1em vez de ∘↑(...)¨e, alterando o formato de entrada a partir de uma matriz aninhada para uma matriz

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

Experimente online!

Recebe a entrada como uma matriz de caracteres como o argumento correto, em que cada string de versão está em sua própria linha. Saídas ¯1 1, 0 0, 1 ¯1para <, =, >respectivamente.

(⍎¨∊∘⎕D⊆⊢)⍤1 em cada linha

  • ∊∘⎕D⊆⊢ agrupe todas as ocorrências de dígitos, ou seja, divida em .

  • ⍎¨ e converta cada uma dessas ocorrências em um número

converter para uma matriz, onde a primeira entrada está na linha superior e a segunda na parte inferior, preenchendo com 0s quando necessário

(⍋-⍒) e

  • - subtrair
    • os índices nas linhas que os ordenariam em ordem decrescente
    • igual ao topo, mas por ordem crescente

4

Perl 6 , 63 47 22 bytes

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

Experimente online!

Acontece que o Perl 6 tem um tipo de versão que praticamente se encaixa na descrição. Este é um bloco de código anônimo que pega uma lista de duas seqüências de versão e retorna ou More, Sameou Less.

Explicação:

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

Ou, sem tipos internos para 47 bytes:

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

Experimente online!

Bloco de código anônimo que pega duas cadeias e retorna Morese a segunda for maior, Lessse a segunda for menor e Nilse forem iguais.

Explicação:

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

Braquilog , 49 40 bytes

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

... Ainda é bastante inexpressivo.

Espera uma lista de duas cadeias. Usa positive number / zero / negative number como > / = / <.

Experimente online!

Explicação

Dividindo as entradas

Dada uma entrada que não se une [0, 0], como, por exemplo ["1.02.0", "1.2.0.1.0"], as saídas do segmento abaixo, por exemplo [[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]

Comparando as entradas

Dado, por exemplo, [[1, "02.0"], [1, "2.0.1.0"]]fecha os sublistas [[1, 1], ["02.0", "2.0.1.0"]]e compara os valores na cabeça ( [1,1]). Recorra na segunda sublist. Observe que o predicado zip zpercorre listas mais curtas, de modo que fechar com [0,0]é equivalente a fechar com [0], portanto, a etapa anterior se unifica 0com 0sem valores adicionais anexados.

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 graças a @redundancy

(a)(b)0 0

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

Experimente online!


Agradável. Se entendi corretamente, você pode salvar bytes substituindo replacepor fill. Os operandos de -são trocados, pois agora ambos devem ser coagidos a um número. Experimente online!
redundância

@redundancy Boa ideia! (Não tenho certeza se minha aplicação é exatamente o que você tinha em mente, no entanto.)
Arnauld

Presumi que sua intenção era acrescentar valores suficientes coercíveis a 0, de modo que o mapeamento sobre as substrings de aeventualmente alterne entre esses valores 0 se bcontiver mais segmentos numéricos que a. Acontece que o método mais curto de garantir isso é dividir-se em uma bcadeia de comprimento de '.' aproveitando a divisão existente aplicada a a.
redundância

3

Java (JDK 10) , 201 96 89 bytes

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

Experimente online!

Retorna um número negativo se a primeira versão for menor que a segunda, um positivo se a primeira versão for maior que a segunda e 0se forem iguais.

Sim, isso é um trabalho pesado para "apenas" chamar um built-in!

Créditos


1
Eu tentei, mas eu sou capaz de remover apenas três bytes. 228 bytes
Kevin Cruijssen

1
Encontrou algo mais: 217 bytes
Kevin Cruijssen

1
Provavelmente é isso. Já tentei, try-finallypara que a verificação do if possa ser simplificada; tentei retornar dentro do loop if t!=0; tentei usar Integere i.compare(i.valueOf(...),i.valueOf(...)); tentou usar genéricos como este <T>T[]g(T s){return(T[])(s+"").replaceAll("(\\.0+)*$","").split("\\.");}; etc. Todos têm de 2 a 6 bytes. Se você (ou qualquer outra pessoa) encontrar algo mais, entre em contato. Curioso para saber o que. :)
Kevin Cruijssen

1
@KevinCruijssen Não, não posso porque "Todos os números em um número de versão seriam menores que 2^16". Curtos intervalos de - (2 ^ 15) a 2 ^ 15-1.
Olivier Grégoire

1
@KevinCruijssen Eu poderia remover 105 bytes! Quão? Bem, eu encontrei um built-in;)
Olivier Grégoire


2

Retina 0.8.2 , 54 bytes

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

Experimente online! O link inclui casos de teste. Usa o valor do separador como saída de igualdade, portanto, por conveniência, o cabeçalho converte o separador de entrada em, =mas pode ser qualquer coisa que não esteja presente [.\d]. Explicação:

\d+
$*

Converta para unário.

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

Exclua repetidamente o primeiro caractere de cada lado até que eles diferam ou um lado acabe. Isso é muito mais rápido do que tentar combinar prefixos, embora possivelmente não seja um jogador de golfe. Nesse ponto, as cadeias estão em uma das várias formas, que precisam ser decodificadas para um resultado de comparação.

  1. Se nenhuma string contiver um 1, o resultado será=
  2. Se a corda esquerda começar com a 1, o resultado será>
  3. Se a sequência correta começar com a 1, o resultado será<
  4. Se a string esquerda estiver vazia, o resultado será <
  5. Nesse ponto, a corda certa está vazia, então o resultado é >

Outra maneira de pensar sobre isso é que, se uma string contém um 1e o outro não inicia com um 1, essa string é maior, no entanto, isso acaba sendo um byte mais longo.

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

Verifique o caso 3 ou o caso 4 sem o caso 1.

.*1.*=.*
>

Se a string esquerda ainda contiver 1a nesse ponto, será maior.

\.

Caso contrário, exclua as sobras de .s.

Console do navegador Firefox REPL, 19 bytes

Services.vc.compare

Acredito que essa função interna realize a comparação necessária. Retorna -1, 0 ou 1.


1
Sugiro que você postar o código cromo Firefox como uma outra resposta ...
TSH

Aliás, não tenho certeza de como o código de cromo do Firefox conta seus bytes. Deve Cu.import("resource://gre/modules/Services.jsm");ser contado?
Tsh

1
@tsh É por isso que eu adicionei "Browser Console REPL" ...
Neil


2

C (gcc) ,  140  134 bytes

Este código emite um negativo, 0ou um positivo para <, =ou >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;}

Experimente online!

Edições:

  • Economizou 6 bytes graças a ceilingcat!

O desafio declara: "Use três valores distintos constantes;" Seu código não retorna constantes.
Olivier Grégoire

1
@ Olivier Ele afirma que eu posso "Use três valores distintos constantes;" OU "Use número positivo / zero / número negativo, enquanto zero significa igual;"
Annyo 22/08/19

Minha culpa! Você está correto.
Olivier Grégoire


1

JavaScript (Node.js) , 105 88 80 bytes

-17 bytes de @redundancy. Uau!

-8 bytes removendo Math.sign. Obrigado @tsh

Retorna um valor negativo, zero ou positivo

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

Experimente online!


1
88 bytes usando execpara dividir seqüências de caracteres. Experimente online!
redundância

@redundancy Porra, obrigado! isso é um truque bem legal
Luis felipe De jesus Munoz

Talvez você queira remover o Math.sign para salvar alguns bytes, alternando para valores positivos / zero / negativos. E talvez seja necessário um sinal positivo.
tsh


0

Limpo , 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)

Experimente online!

Emite um número negativo quando o primeiro argumento é menor que o segundo, zero quando são equivalentes e um número positivo quando é maior que o segundo.


0

Swift 4 , 155 bytes

Cabeçalho (não contado: o código não é 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)}

Experimente online!

Explicações

  • Aparamos à direita .0.
  • Comparamos componentes numericamente.

Constantes retornadas

  • nada para =
  • verdadeiro 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)

Experimente online!

Com comentários:

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

Burlesco - 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

Se você deseja saída em '> <=', adicione ?i"<=>"j!!Q.


0

PowerShell, 88 bytes

Retorna 0para igual, a positive integerpara maior que ou a negative integerpara menor que.

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

Script de teste com menos golfe:

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

Saída:

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

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

Experimente online!

  • -44 bytes usando variáveis ​​para armazenar comprimento e usando loop ternário
  • -2 bytes removendo os colchetes

0

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

Importação (13 bytes, incluindo ;para separar do código):

Isso importará o Foundation, mas é 5 bytes menor que import Foundation.

import UIKit;

Cabeçalho (não contado: o código não é 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)}

Experimente online!

Explicações

  1. Anexamos alguns .0 à direita para o mesmo número de componentes.
  2. Comparamos componentes numericamente.

Constantes retornadas

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

Não tenho certeza se contamos a importdeclaração, então postei uma resposta separada que não exige Foundatione com uma contagem de bytes entre 142 bytes (sem contar a importação) e 160 bytes (sem contar a importação).
Cœur

0

Zsh , 54 bytes

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

Experimente online! Experimente uma suíte de testes!

Trata- evalse das oito declarações a seguir:

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

Portanto, os três valores únicos são:

 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>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.