Decodifica una quantità di lunghezza variabile


16

Una quantità a lunghezza variabile (nota anche come VLQ o uintvar) è un modo per codificare fino a un valore intero a 28 bit utilizzando solo il numero di byte necessario. Questo è stato usato nel formato di file MIDI come un modo per ridurre al minimo le dimensioni di alcuni dati di eventi.

Il modo in cui funziona è abbastanza semplice. Come una serie di byte big-endian, il bit più significativo (MSB) di ciascun byte 1indica che segue un altro byte VLQ. I restanti 7 bit di ciascun byte costituiscono il valore decodificato.

Esempio (da Wikipedia):

[ 0x86, 0xc3, 0x17 ] => 106903

inserisci qui la descrizione dell'immagine Ulteriori riferimenti: Wikipedia , Some Guy .

Sfida:

Data una quantità di lunghezza variabile, convertila nel suo valore intero.

Ingresso:

Un elenco da uno a quattro byte o un tipo di valore a 32 bit che rappresenta un VLQ valido di un numero intero.

Produzione:

Il valore intero dell'ingresso VLQ.

Regole e punteggio:

  • Questo è code-golf, quindi vince la risposta più breve in byte per ogni lingua .
  • Si applicano le regole standard e le regole I / O predefinite .
  • Scappatoie vietate (ovviamente).
  • Fornisci un link con un test per il tuo codice ( TIO.run , ecc.).
  • Si consiglia vivamente una spiegazione chiara della risposta.
  • Gli incorporati che gestiscono questa conversione non sono vietati, tuttavia non usarli è molto più interessante.

Casi test:

Input (VLQ)                   Output (int)

[                   0x00 ] => 0
[                   0x07 ] => 7
[                   0x7f ] => 127
[             0x81, 0x00 ] => 128
[             0xC0, 0x00 ] => 8192
[             0xff, 0x7f ] => 16383
[       0x81, 0x80, 0x00 ] => 16384
[       0x86, 0xc3, 0x17 ] => 106903
[       0xbd, 0x84, 0x40 ] => 1000000
[       0xff, 0xff, 0x7f ] => 2097151 
[ 0xC0, 0x80, 0x80, 0x00 ] => 134217728  
[ 0xFF, 0xFF, 0xFF, 0x7F ] => 268435455  

Nota: non è necessario utilizzare valori letterali esadecimali per rappresentare un byte come input o output. È possibile utilizzare il valore decimale letterale ( [ 129, 128, 0 ]), intero ( 0x80818000) o qualsiasi altra rappresentazione byte / ottetto ragionevole se più adatto alla propria piattaforma. Il formato è flessibile purché rappresenti 1-4 byte / ottetti.

Golf lontano!


L'ultimo byte dell'ingresso è garantito <128? O dobbiamo supportare casi come [0x01, 0x80, 0x02] => 1?
Arnauld,

5
@ArnaAvrei visto meglio quello a cui stavi arrivando e, a posteriori, il caso che hai menzionato sarebbe stato bello aver richiesto. Nel MIDI, ad esempio, leggerai un flusso di dati e quindi non sai necessariamente quale byte è l'ultimo. Il modo in cui è scritto, garantendo che l'ultimo byte dell'elenco sia l'ultimo byte nel VLQ rende l'MSB irrilevante e, in effetti, sconfigge lo scopo del VLQ. Come in, non saresti necessariamente in grado di usare queste routine (valide per la sfida) per la decodifica dei file MIDI. Totalmente mio cattivo! Avrei dovuto tenerlo nella sandbox molto più a lungo ...
640 KB

5
Questo è in parte anche negativo perché penso di aver visto la sfida nella sandbox e di non aver prestato sufficiente attenzione. Ma sì, questo è quello che avevo in mente: dato un elenco di byte, o output del primo valore VLQ o output di tutti i valori VLQ .
Arnauld,

1
@KevinCruijssen, una quantità di lunghezza variabile dovrebbe essere un flusso di byte, dove tecnicamente sai solo quando termina raggiungendo l'indicatore MSB = 0. So che le regole non lo hanno del tutto catturato, ma per definizione di VLQ dovrò dire di no.
640 KB

1
@gwaugh Ok, ha senso e posso capire il ragionamento. Modificherò di conseguenza la mia risposta MathGolf. :)
Kevin Cruijssen l'

Risposte:





4

J , 10 byte

128#.128|]

Provalo online!

Ispirandosi alla risposta APL di J Salle.

  • 128|] Resto dei numeri di input divisi per 128
  • 128#. Interpretato come le cifre di un numero di base 128


3

05AB1E , 6 byte

žy%žyβ

Provalo online!

12827


Sì, quel builtin è inutile al giorno d'oggi. Nella versione legacy ho potuto vedere un po 'di utilizzo, solo quando hai una cifra prima di essa nel tuo programma. Altrimenti potresti semplicemente usare 7o. Al giorno d'oggi è possibile comprimere alcuni numeri interi a 3 byte (intervallo [101,355]) in 2 byte, quindi 128 può essere ƵRcomunque .. Mi sono anche chiesto la stessa cosa sull'integrato a 2 byte per 16 .. Di solito useresti semplicemente il valore letterale o altrimenti avremmo 4o/ 4n/ se nel programma è presente una cifra. Solo quando una cifra è prima del 16, cosa che non credo succederebbe, l'incorporazione è utile ..
Kevin Cruijssen


2

JavaScript (ES6), 29 byte

-2 byte grazie a @Shaggy

Accetta l'input come una matrice di byte.

a=>a.map(p=c=>p=p<<7|c&127)|p

Provalo online!


2

APL + WIN, 22 byte

Richiede un vettore di numeri interi:

2⊥¯32↑,0 1↓⍉(8⍴2)⊤¯4↑⎕

Provalo online! Per gentile concessione di Dyalog Classic

Spiegazione:

¯4↑⎕ Pad the vector to 4 integers from the right.

⍉(8⍴2)⊤ Convert to a matrix of 8 bit values.

,0 1↓ drop the MSBs and flatten to a vector.

2⊥¯32↑ pad bit vector to 32 bits starting at LSB and convert back to integer.

2

Stax , 12 byte

ü╫ôà¡k2Wù}a☺

Eseguilo e esegui il debug su staxlang.xyz!

Spacchettato (14 byte) e spiegazione:

rk128%128i^#*+
r                 Reverse 'cuz big-endian
 k                Fold from the left using block:
  128%              Modulize by 128
      128i^#        Push 128 to the power of (one plus the iteration index)
            *       Multiply
             +      Add to the total
                  Implicit print

Stax ha una conversione di base integrata, ma funziona solo su stringhe. Funziona quasi su liste di numeri interi, però; il problema è nella gestione di Stax 0.

Una stringa è un elenco di numeri interi. Quando si utilizza un tale elenco come stringa, tutti gli zero vengono automaticamente convertiti in 32 come una scorciatoia per gli spazi. Poiché l'integrato |bper la conversione di base considera il suo operando come una stringa anziché come un elenco non elaborato di numeri interi, qualsiasi caso con zero non riuscirà.

10 byte, errore sugli zero

Ç┘_A♥∙QZ►╣
{128%m128|b    Unpacked
{128%m         Modulize each by 128
      128|b    Convert from base 128

Eseguilo e esegui il debug su staxlang.xyz!


1
Vedo da dove viene questo problema. {:B7)m$:bpacco a 8, e sembra funzionare anche, anche se è un po 'esotico $.
ricorsivo

@recursive È intelligente. Posta come una risposta separata.
Khuldraeseth na'Barya,


2

C (gcc) , 48 byte

Accetta un numero intero nell'ordine big-endian come input, che è lo stesso ordine di un array di byte.

f(i,j){for(j=0;j|=i&127,i&128;j<<=7,i>>=8);i=j;}

Provalo online!

C (gcc) , 53 byte

Se è necessario un array di byte:

f(c,j)char*c;{for(j=0;j|=*c&127,*c++&128;j<<=7);c=j;}

Provalo online!


Quando compilo il tuo codice di test TIO con -Os gestisce solo gli ultimi due casi. Non conosco abbastanza C per dire il perché.
qwr

Per impostazione predefinita -O0, @qwr TIO consente di memorizzare (di solito) un valore di ritorno nel primo parametro. Questa è una stranezza ^ Funzionalità nel golf di codice, ma non funziona con livelli di ottimizzazione più elevati.
ErikF

Puoi radere un byte sostituendolo &128con >>7.
G. Sliepen,

2

MathGolf , 14 byte

ê♣%hrx♣▬^mÅε*Σ

Immettere come numeri interi.

Provalo online.

Ho la sensazione che questo possa essere più breve .. È un po 'fastidioso che MathGolf abbia un byte incorporato per la costante 128 , ma nessuna conversione di base (tranne binaria / esadecimale).

Spiegazione:

ê              # Take the inputs as integer-array
 ♣%            # Take modulo-128 on each value in this array
   h           # Push the length of the array (without popping the array itself)
    r          # Pop and push a list in the range [0, length)
     x         # Reverse the array to (length, 0]
      ♣▬       # Take 128 to the power of each value in this array
        ^      # Zip the two lists together to create pairs
         mÅ    # Map each pair to (using the following two commands):
           ε*  #  Reduce by multiplication (so basically the product of the pair)
             Σ # After multiplying each pair, take the total sum
               # (after which the entire stack joined together is output implicitly)

La conversione di base è sul tavolo dal primo giorno, ma ci sono alcune altre revisioni riguardanti la conversione di base che voglio affrontare contemporaneamente. Ad esempio, non desidero che operatori diversi convertano in / da stringhe esadecimali.
max

2

Python 3 , 58 49 byte

-9 byte grazie a @Chas e @ ar4093

lambda x:int("".join(bin(a|128)[3:]for a in x),2)

Provalo online!

o

lambda x:int("".join(f"{a%128:07b}"for a in x),2)

Provalo online!

Immissione tramite elenco di numeri interi.

La binfunzione di Python aggiunge "0b" all'inizio della stringa, quindi devono essere tolti prima che possano essere concatenati. Inoltre non mantiene gli zeri iniziali, quindi se non ce ne sono (ovvero l'ultimo byte), questi devono essere aggiunti nuovamente. E se ce n'è uno iniziale (ovvero tutti tranne l'ultimo byte) che deve essere rimosso come bene. Grazie a @Chas per aver capito che impostando sempre il primo bit, posso semplicemente rimuovere i primi tre caratteri ed essere fatto.

Apparentemente (secondo @ ar4093) la formatfunzione consente non solo di non avere il prefisso '0b', ma anche di rimuovere il primo bit e di riempire con 7 caratteri contemporaneamente.


Benvenuto in CGCC, che ti renderà un programmatore molto peggio! :). All'inizio ho pensato come te. È possibile salvare 9 byte con bin(a|128)[3:]poiché non è necessario zfill.
Chas Brown,

Come hai detto, con la funzione di formattazione è possibile salvare 9 byte: bin(a)[2:].zfill(8)[1:]->f"{a%128:07b}"
ar4093


1

Japt , 10 8 byte

Accetta l'input come una matrice di numeri interi.

muIÑ ìIÑ

Provalo o esegui tutti i casi di test (intestazione in entrambi i convertiti dal formato di input utilizzato nella sfida)

Hai salvato 2 byte prendendo ispirazione dalla soluzione di alephalpha .

muIÑ ìIÑ     :Implicit input of integer array
m            :Map
 u           :  Modulo
  I          :    64
   Ñ         :    Multiply by 2
     ì       :Convert to decimal
      IÑ     :  From base 64*2

1

Carbone , 11 byte

I↨﹪θ¹²⁸¦¹²⁸

Provalo online! Il collegamento è alla versione dettagliata del codice. Accetta input come un array. Spiegazione:

   θ        Input array
  ﹪ ¹²⁸    Elementwise modulo 128
 ↨      ¹²⁸ Convert from base 128
I          Cast to string for implicit print


1

Batch di Windows, 76 byte

:a
@set/ax="%1&127",y=y*128+x
@if %2. neq . @shift&goto:a
@echo %y%

Passare i parametri con il prefisso "0x" e spazio tra (es. 0xC0 0x80 0x80 0x00).


Ben fatto. Ovviamente per chiunque lo provi, devi assicurarti di fare una @set y=,ax=tra una corsa e l' altra .
640 KB

1
basta "set y =".
peter ferrie il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.