ООП: перекрывающееся ориентированное программирование


32

Одной из малоизвестных парадигм программирования, которая кажется весьма подходящей для игры в код, является перекрывающееся ориентированное программирование (ООП) *. При написании частично идентичного кода многие байты можно сохранить, просто перекрывая идентичные части и запоминая каким-то образом, где начинаются две строки исходного кода. Ваша задача состоит в том, чтобы написать две пересекающиеся программы или функции compressи decompressсо следующими характеристиками:

* Не используйте в производственном коде, вероятно.

compress

compressберет две строки в любом удобном формате и максимально перекрывает их. То есть строка sс минимальной длиной возвращается так, что обе входные строки являются подстрока s. Кроме того, возвращается некоторый вывод, который идентифицирует начальный и конечный индексы обеих строк.

Примеры: (Точный IO-формат зависит от вас)

compress("abcd", "deab") -> "deabcd" ((2,5),(0,3))
compress("abcd", "bc")   -> "abcd" ((0,3),(1,2))
compress("abc", "def")   -> "abcdef" ((0,2),(3,5)) or "defabc" ((3,5),(0,2))

decompress

decompressвычисляет обратную функцию compress, которой дается строка и два начальных и конечных индекса (в формате, в котором они возвращаются вашим compress), возвращают две исходные строки. Вам нужно только обрабатывать действительные входные данные. Равенство должно выполняться для всех строк s1, s2:

(s1, s2) == decompress (compress (s1, s2))

Примеры: (обратные compressпримеры)

decompress "deabcd" ((2,5),(0,3)) -> "abcd" "deab" 
decompress "abcd" ((0,3),(1,2))   -> "abcd" "bc"

decompress "abcdef" ((0,2),(3,5)) -> "abc" "def"   
 or (whichever version your "compress" generates)
decompress "defabc" ((3,5),(0,2)) -> "abc" "def"

счет

Ваша оценка - это размер строки, возвращаемой вызовом compress("<code of compress>", "<code of decompress>"). Так как это ниже оценка.

Пример:

Предположим, что код для вашей функции compressis c=abcdи код для decompressis d=efghi. Затем compress("c=abcd", "d=efghi")дает "c=abcd=efghi"(и индексы, но те, которые не влияют на выигрыш), так что счет length "c=abcd=efghi" = 12.

Дополнительные правила

  • В духе этого вызова ваши compressи decompress должны совпадать хотя бы в одном персонаже. Вы можете достичь этого тривиально, добавив комментарий, но учтите, что это увеличит ваш счет, и могут быть более короткие решения, использующие частично совпадающий код.
  • compressи decompressдолжен иметь возможность обрабатывать строки, содержащие любые печатные символы ASCII, а также все символы, которые вы использовали для определения compressи decompress.
  • Индексы могут быть нулевыми или одноиндексными.
  • Ваши программы или функции не обязательно должны быть названы compressи decompress.

Можете ли вы использовать разные аргументы командной строки для запуска сжатия и распаковки кода?
MildlyMilquetoast

Конечно. Вы должны указать две программы, а политика сайта допускает аргументы командной строки, пока они учитываются, поэтому вы можете задать разные аргументы командной строки для каждой из ваших программ.
Лайкони

Ответы:


25

GNU Пролог, 105 баллов

s(U,L/M,C):-prefix(A,C),length(A,M),suffix(U,A),length(U,L).
o(A-B,C-X-Y):-length(C,_),s(A,X,C),s(B,Y,C).

(Это требует GNU Prolog, потому что prefixи suffixне переносимы.)

Пролог имеет одно интересное, главное преимущество для этой задачи; Вы можете написать функцию для обработки нескольких шаблонов вызовов (т. е. вы можете не только дать функции вход для получения соответствующего выхода, вы можете дать функции выход для получения соответствующего ввода). Таким образом, мы можем определить функцию, которая может обрабатывать как сжатие, так и распаковку, что приводит к 105-байтовой передаче, которая определяет функцию, oкоторая работает в обоих направлениях. (Между прочим, я в основном написал это как декомпрессор - как это проще - и получил компрессор «бесплатно».) В общем, мы могли бы ожидать очень короткую программу на Прологе для этой задачи, если бы не тот факт, что она настолько плохая при обработке строк (как с точки зрения отсутствующих примитивов, так и с точки зрения рассматриваемых примитивов, имеющих ужасно длинные имена).

Первый аргумент o- это кортеж строк, например "abcd"-"deab". Второй аргумент имеет форму, похожую на"deabcd"-4/6-4/4 ; это довольно стандартный вложенный кортеж, и это означает, что строка «deabcd», первая строка имеет длину 4 и заканчивается шестым символом, вторая строка имеет длину 4 и заканчивается четвертым символом. (Обратите внимание, что строка в GNU Prolog - это просто список кодов символов, что делает отладку раздражающей, потому что реализация предпочитает последнюю интерпретацию по умолчанию.) Если вы дадитеoодин аргумент, он выведет другой для вас (таким образом, работая как компрессор, если вы дадите первый аргумент, и как декомпрессор, если вы дадите второй). Если вы дадите ему оба аргумента, он проверит, что сжатое представление соответствует заданным строкам. Если вы дадите ему нулевые аргументы, он сгенерирует вывод примерно так:

| ?- o(X,Y).
X = []-[]
Y = []-0/0-0/0 ? ;

X = []-[]
Y = [_]-0/0-0/0 ? ;

X = []-[A]
Y = [A]-0/0-1/1 ? ;

many lines later

X = [A]-[B,A,C]
Y = [B,A,C]-1/2-3/3 ? ;

Вышеприведенное описание формата ввода / вывода также в значительной степени является всего лишь описанием программы; в программе не так много всего. Единственная тонкость связана с подсказками порядка оценки; нам нужно убедиться, что программа не только использует стратегию поиска, которая гарантированно завершается, но и выдает максимально короткую строку вывода.

При сжатии мы начинаем с length(C,_)Cимеет длину»), что является уловкой, которую я использовал во многих ответах на пролог и брахилог; если это первое, что видит Пролог, это заставит его расставить приоритеты, уменьшив длину Cнад чем-либо еще. Это гарантирует, что у нас есть минимальная длина C. Порядок ограничений в sтщательно выбран, так что поиск займет конечное время для каждой возможной длины кандидата C; Aсдерживается C(мы не знаем C, но мы знаем , целевое значение , которое мы имеем для ее длины), Mна A, Uпо A, и Lпо U, так ни один из запросов не может принимать неограниченное время.

При распаковке мы даем Cнепосредственно пользователю. Это снова гарантирует, что программа будет работать за конечное время из-за той же последовательности ограничений. (Люди, которые знают о порядке оценки Пролога, заметят, что определение sпри распаковке очень неэффективно; размещение length(A,M)и length(U,L)первое будет быстрее, но перемещение length(A,M)к началу может вызвать бесконечный цикл при сжатии, потому что в то же время Aне Mсвязано ни с чем, ни с чем-либо связанным .)


13

Брахилог , 50 46 байт

{Ċ∧Lċ₂l∧Lgj:?z{tT∧?h~cṪhlI∧ṪbhTl:I+-₁:I↔}ᵐ:L}

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

Распаковка:

~{Ċ∧Lċ₂l∧Lgj:?z{tT∧?h~cṪhlI∧ṪbhTl:I+-₁:I↔}ᵐ:L}

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

Сохранено 5 байтов благодаря @ ais523

объяснение

Хорошая сторона декларативных языков заключается в том, что мы можем повторно использовать один и тот же код как для сжатия, так и для распаковки. Таким образом, код для сжатия точно такой же, как и для распаковки , с дополнительным ~в начале.

Это ~говорит Brachylog об обратном порядке аргументов (то есть, использовать входные данные как выходные данные и выходные данные как входные данные). Поскольку сжатие не имеет ~, он фактически выполняет предикат в стандартном порядке. Поскольку распаковка имеет только один, он запускает его с входом в качестве вывода и выходом в качестве ввода.

Таким образом, мы можем использовать один и тот же код (по модулю extra ~) как для сжатия, так и для распаковки: сжатие обеспечивает две строки в качестве входных данных, а переменную - в качестве выходных, а декомпрессия - предоставление индексов и сжатой строки в качестве выходных данных, а переменную - в качестве входных данных. ,

Очевидно, это также означает, что мы должны быть немного откровенны в отношении нашего кода сжатия, чтобы интерпретатор мог запускать его «назад». Вот почему сам компрессор немного длинный.

Вот разбивка кода для Compress (и, следовательно, также для распаковки):

{……………………………………………………………………}   Call that predicate the normal way (with swapped arguments
                                 for decompress)
   Ċ                           Input has two elements
   ∧Lċ₂l                       L is a string of any length (measuring its length forces it to
                                 take a specific length from 0 to +inf)
   ∧Lgj                        The list [L,L]
       :?z                     The list [[L, First elem of Input],[L,second elem of input]]
          {………………………………}ᵐ:L    Final output is the [M,L] where M is the result of mapping
                                 the predicate below on both elements of the zip
           tT                  The second element of the input is T
           ∧?h~cṪ              Anticoncatenate the first element of the input into [A,B,C]
           hlI                 I = length(A)
           ∧ṪbhTl:I+-₁         J = length(T) + I - 1
           :I↔                 Output = [I,J]

4

Желе , 58 50 байт

-1 байт благодаря ais523 (используйте для двухбайтовой строки)

Это вполне может быть вполне пригодным для игры в гольф ...

Сжатие принимает два строковых аргумента и возвращает список:
[[[startA, lengthA], [startB, lengthB]], compressedString]

w³;w⁴$
0;J⁸ḣ;€
ç;ç@ÑẠ$ÐfLÐṂṪµ³,⁴L€ż@Ñ,

Декомпрессия принимает один аргумент (такой список) и возвращает две * строки:

,
⁾ṫḣżFv
Ḣç€Ṫ

Перекрытый код:

w³;w⁴$
0;J⁸ḣ;€
ç;ç@ÑẠ$ÐfLÐṂṪµ³,⁴L€ż@Ñ,
⁾ṫḣżFv
Ḣç€Ṫ

Один индексированные.

* это может быть неочевидно из-за неявного форматирования печати Jelly, поэтому код в TryItOnline, на который ссылается выше, имеет дополнительный байт (a Yв конце) для вставки перевода строки между этими двумя в печатном выводе.

Я начал с одной программы, которая использовала глубину первого (или единственного) аргумента для выбора между сжатием и распаковкой, но наличие неиспользуемой ссылки в коде распаковки (первая строка) и одного перекрывающего байта короче на семь байтов ,

Как?

ç;ç@ÑẠ$ÐfLÐṂṪµ³,⁴L€ż@Ñ, - Compression: stringA, stringB
ç                       - call the last link (2) as a dyad
  ç@                    - call the last link (2) as a dyad with reversed arguments
 ;                      - concatenate (gives all overlapping strings)
       Ðf               - filter keep:
      $                 -     last two links as a monad
    Ñ                   -         call the next link (1) as a monad
     Ạ                  -         All? (no zeros exist in that result)
          ÐṂ            - filter keep with minimal:
         L              -     length
            Ṫ           - tail (for when more than one exists)
             µ          - monadic chain separation (we now have the compressed string)
              ³,⁴       - [stringA, stringB]
                 L€     - length of €ach
                   ż@   - zip with reversed arguments with
                     Ñ  - next link (1) as a monad with the compressed string
                      , - paired with the compressed string

J0;⁸ḣ;€ - Link 2, possible overlaps: stringL, stringR
J       - range(length(stringL)) - [1,2,...,length(stringL)]
 0;     - zero concatenate       - [0,1,2,...,length(stringL)]
   ⁸    - stringL
    ḣ   - head (vectorises)      - [empty string, first char, first two, ..., stringL]
     ;€ - concatenate €ach with stringR

w³;w⁴$ - Link 1, substring indexes: stringX
w³     - first index of first program argument in stringX or 0 if not found
  ;    - concatenated with
     $ - last two links as a monad
   w⁴  -     first index of second program argument in stringX or 0 if not found
Ḣñ€Ṫ - Decompression: [[[startA, lengthA], [startB, lengthB]], compressedString], ?
Ḣ    - head - [[startA, lengthA], [startB, lengthB]]
   Ṫ - tail - compressedString
 ç€  - call the last link (2) as a dyad for €ach of the left list
     -- extra Y atom at TIO joins the resulting list of two strings with a line feed.

⁾ṫḣżFv - Link 2, extract a substring: [start, length], string
⁾ṫḣ    - string "ṫḣ"
   ż   - zip with [start, length] to yield [['ṫ', start],['ḣ', length]]
    F  - flatten, making a list of characters
     v - evaluate as Jelly code with the string as an argument
       - this evaluates as string.tail(start).head(length) yielding the substring

, - Link 1: only here to make an overlap with the compression program.

“ṫḣ”может быть обработано на 1 байт с помощью синтаксиса Jelly для 2-символьных строк.

Это вопрос, совершенно не связанный с ответом как таковым, но вы пишете объяснение кода вручную или есть инструмент, генерирующий его из кода?
tfrascaroli

@tfrascaroli Я пишу это от руки
Джонатан Аллан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.