Декодировать переменную длину


16

Величина переменной длины (также упоминается как VLQ или uintvar) способ кодировать до 28 битное целое значение , используя только столько байт , сколько необходимо. Это использовалось в формате файла MIDI как способ минимизировать размер определенных данных события.

Как это работает, довольно просто. В качестве последовательности байтов с 1прямым порядком байтов старший значащий бит (MSB) каждого байта представляет собой a, чтобы указать, что следует другой байт VLQ. Оставшиеся 7 бит каждого байта составляют декодированное значение.

Пример (из Википедии):

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

введите описание изображения здесь Дополнительные ссылки: Википедия , Some Guy .

Вызов:

Если дано значение переменной длины, преобразуйте его в целочисленное значение.

Входные данные:

Список от одного до четырех байтов или 32-битный тип значения, представляющий действительный VLQ целого числа.

Выход:

Целочисленное значение входа VLQ.

Правила и оценки:

  • Это код-гольф, поэтому выигрывает самый короткий ответ в байтах для каждого языка .
  • Применяются стандартные правила и правила ввода / вывода по умолчанию .
  • Лазейки запрещены (конечно).
  • Пожалуйста, предоставьте ссылку с тестом для вашего кода ( TIO.run и т. Д.).
  • Четкое объяснение вашего ответа настоятельно рекомендуется.
  • Встроенные модули , которые обрабатывают это преобразование не запрещены, однако не их использования является намного более интересным.

Тестовые случаи:

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  

Примечание. От вас не требуется использовать шестнадцатеричные литералы для представления байта в качестве входных или выходных данных. Вы можете использовать десятичное литерал ( [ 129, 128, 0 ]), целое число ( 0x80818000) или любое другое разумное представление байта / октета, если оно лучше подходит для вашей платформы. Формат гибкий, если он представляет 1-4 байта / октета.

Гольф прочь!


Последний байт ввода гарантированно будет <128? Или нам нужно поддерживать такие случаи, как [0x01, 0x80, 0x02] => 1?
Арно

5
@Arnauld Теперь я лучше вижу, к чему ты клонишь, и, оглядываясь назад, случай, о котором ты говоришь, был бы полезен. Например, в MIDI вы будете читать поток данных, поэтому вы не обязательно будете знать, какой байт является последним. То, как оно написано, гарантирует, что последний байт в списке является последним байтом в VLQ, что делает MSB неактуальным и фактически отчасти отрицает цель VLQ. Например, вы не обязательно сможете использовать эти (действительные для вызова) процедуры для декодирования файлов MIDI. Абсолютно мой плохой! Надо было хранить это в песочнице гораздо дольше ...
640KB

5
Отчасти это тоже плохо, потому что я думаю, что видел проблему в песочнице и не уделял достаточно внимания. Но да, это то, что я имел в виду: учитывая список байтов, либо выведите первое значение VLQ, либо выведите все значения VLQ .
Арно

1
@KevinCruijssen, величина переменной длины должна быть потоком байтов, где технически вы знаете, когда он заканчивается, достигнув маркера MSB = 0. Я знаю, что правила не совсем отражают это, но по определению VLQ мне придется сказать «нет».
640KB

1
@gwaugh Хорошо, это имеет смысл, и я могу понять причину. Я изменю свой ответ MathGolf соответственно. :)
Кевин Круйссен

Ответы:





4

J , 10 байт

128#.128|]

Попробуйте онлайн!

Вдохновленный ответом APL J Salle.

  • 128|] Остаток входных чисел, разделенных на 128
  • 128#. Интерпретируется как цифры базового 128 числа


3

05AB1E , 6 байтов

žy%žyβ

Попробуйте онлайн!

12827


Да, это встроенное в настоящее время бесполезно. В прежней версии я мог видеть ее использование, только когда в вашей программе перед ней стоит цифра. В противном случае вы могли бы просто использовать 7o. В настоящее время вы можете сжать некоторые 3-байтовые целые числа (диапазон [101,355]) в 2 байта, так что 128 может быть в ƵRлюбом случае ... Я также удивляюсь тому же, что встроенная 2-байтовая функция для 16 ... Обычно вы просто используете литерал , или в противном случае мы бы 4o/ 4n/ если цифра за ней в программе. Только когда цифра перед 16, что, я думаю, не произойдет, встроенная
функция




2

Stax , 12 байт

ü╫ôà¡k2Wù}a☺

Запустите и отладьте его на staxlang.xyz!

Распаковывается (14 байт) и объяснение:

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 имеет встроенное базовое преобразование, но работает только со строками. Это почти работает со списками целых чисел, хотя; проблема в обработке Stax's 0.

Строка - это список целых чисел. Когда вы используете такой список в виде строки, любые нули автоматически преобразуются в 32, что является хорошим сокращением пробелов. Поскольку встроенное |bпреобразование для базы обрабатывает свой операнд как строку, а не как необработанный список целых чисел, любой случай с нулем завершится неудачей.

10 байт, обнуляется

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

Запустите и отладьте его на staxlang.xyz!


1
Я вижу, откуда появилась эта проблема. {:B7)m$:bПакет до 8, и, кажется, работает, хотя это своего рода экзотическое использование $.
рекурсивный

@recursive Это умно. Опубликуйте это как отдельный ответ.
Хулдраесет на'Барья


2

C (gcc) , 48 байтов

Принимает в качестве входных данных целое число в порядке с прямым порядком байтов, которое соответствует порядку байтового массива.

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

Попробуйте онлайн!

C (gcc) , 53 байта

Если требуется байтовый массив:

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

Попробуйте онлайн!


Когда я компилирую ваш код тестирования TIO с -Os, он обрабатывает только два последних случая. Я не знаю достаточно C, чтобы сказать, почему.
qwr

@qwr TIO по умолчанию -O0, что позволяет (обычно) сохранять возвращаемое значение в первом параметре. Это странная штука в игре в гольф, но она не работает с более высокими уровнями оптимизации.
ErikF

Вы можете сбрить байт, заменив &128на >>7.
Г. Слипен

2

MathGolf , 14 байтов

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

Введите как целые числа.

Попробуйте онлайн.

У меня такое ощущение, что это может быть короче. Немного раздражает, что MathGolf имеет встроенную 1-байтовую константу 128, но без преобразования базы (кроме двоичного / шестнадцатеричного).

Объяснение:

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

Базовое преобразование находится на столе с 1-го дня, но есть некоторые другие изменения, касающиеся базового преобразования, которые я хочу рассмотреть в то же время. Например, я не хочу, чтобы разные операторы конвертировали в / из шестнадцатеричных строк.
maxb

2

Python 3 , 58 49 байт

-9 байт благодаря @Chas и @ ar4093

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

Попробуйте онлайн!

или

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

Попробуйте онлайн!

Ввод через список целых чисел.

binФункция Python добавляет «0b» в начало строки, поэтому их необходимо удалить, прежде чем их можно будет объединить. Кроме того, он не сохраняет начальные нули, поэтому, если их нет (так называемый последний байт), они должны быть добавлены обратно. И если есть ведущий (он же все, кроме последнего байта), который должен быть удален как Что ж. Спасибо @Chas за то, что мы выяснили, что всегда устанавливая первый бит, я могу просто удалить первые три символа и все готово.

Очевидно (согласно @ ar4093) formatфункция позволяет не только не иметь префикс '0b', но также удалять первый бит и заполнять до 7 символов одновременно.


Добро пожаловать в CGCC, который сделает вас намного хуже программиста! :). Сначала у меня была такая же мысль, как и у вас. Вы можете сохранить 9 байтов, так bin(a|128)[3:]как тогда вам не нужно zfill.
Час Браун

Как вы сказали, с помощью функции форматирования вы можете сохранить 9 байтов: bin(a)[2:].zfill(8)[1:]->f"{a%128:07b}"
ar4093


1

Japt , 10 8 байт

Принимает ввод как массив целых чисел.

muIÑ ìIÑ

Попробуйте или запустите все тесты (заголовок в обоих конвертируется из входного формата, используемого в вызове)

Сохранение 2 байтов путем вдохновения из раствора алефальфа .

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

Пакет Windows, 76 байт

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

Передайте параметры с префиксом «0x» и пробел между ними (например, 0xC0 0x80 0x80 0x00).


Красиво сделано. Очевидно, что для любого другого, кто его тестирует, вам нужно сделать @set y=,ax=промежуточный прогон.
640KB

1
достаточно «установить y =».
Питер Ферри
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.