OOP: Überlappende orientierte Programmierung

32

Eines der weniger bekannten Programmierparadigmen, das für das Code-Golfen eher geeignet zu sein scheint, ist das Overlapping Oriented Programming (OOP) *. Wenn Sie teilweise identischen Code schreiben, können viele Bytes gespeichert werden, indem Sie einfach identische Teile überlappen und sich merken, wo die beiden ursprünglichen Codezeilen beginnen. Ihre Aufgabe ist es zwei zu schreiben überlappende Programme oder Funktionen compressund decompressmit der folgenden Spezifikation:

* Im Seriencode wahrscheinlich nicht verwenden.

compress

compressNimmt zwei Zeichenfolgen in einem beliebigen Format und überlappt sie so weit wie möglich. Das heißt, eine Zeichenfolge smit minimaler Länge wird zurückgegeben, sodass beide Eingabezeichenfolgen Teilzeichenfolgen von sind s. Außerdem wird eine Ausgabe zurückgegeben, die den Start- und den Endindex beider Zeichenfolgen identifiziert.

Beispiele: (Das genaue IO-Format liegt bei Ihnen)

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

decompressberechnet die Umkehrfunktion von compress, dh , wenn ein String und zwei Start- und Endindizes (in dem Format, in dem sie von Ihrem zurückgegeben werden compress) angegeben werden, geben Sie die beiden ursprünglichen Strings zurück. Sie müssen nur gültige Eingaben verarbeiten. Die folgende Gleichheit für alle Saiten halten soll s1, s2:

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

Beispiele: (Umkehrung der compressBeispiele)

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"

Wertung

Ihre Punktzahl entspricht der Größe der Zeichenfolge, die beim Aufruf zurückgegeben wird compress("<code of compress>", "<code of decompress>"). Da dies eine niedrigere Punktzahl besser.

Beispiel:

Angenommen, der Code für Ihre Funktion compressist c=abcdund der Code für decompressist d=efghi. Dann compress("c=abcd", "d=efghi")ergibt sich "c=abcd=efghi"(und die Indizes, aber diese haben keinen Einfluss auf die Punktzahl), so dass die Punktzahl ist length "c=abcd=efghi" = 12.

Zusätzliche Regeln

  • Im Geiste dieser Herausforderung muss sich Ihr compressund in mindestens einem Charakter decompress überschneiden . Sie können dies trivial erreichen, indem Sie einen Kommentar hinzufügen. Beachten Sie jedoch, dass dies Ihre Punktzahl erhöht und es möglicherweise kürzere Lösungen gibt, bei denen sich inhärent überlappender Code verwendet wird.
  • compressund decompresssollte in der Lage sein, Zeichenfolgen zu verarbeiten, die druckbare ASCII-Zeichen sowie alle Zeichen enthalten, die Sie zum Definieren von compressund verwendet haben decompress.
  • Die Indizes können null- oder einindexiert sein.
  • Ihre Programme oder Funktionen müssen nicht unbedingt benannt sein compressund decompress.
Laikoni
quelle
Können Sie verschiedene Befehlszeilenargumente verwenden, um Code zu komprimieren und zu dekomprimieren?
MildlyMilquetoast
Sicher. Sie müssen zwei Programme angeben, und die Site-Richtlinie lässt Befehlszeilenargumente zu, solange sie gezählt werden, sodass Sie für jedes Ihrer Programme unterschiedliche Befehlszeilenargumente angeben können.
Laikoni

Antworten:

25

GNU Prolog, 105 Punkte

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

(Dies erfordert GNU Prolog da prefixund suffixnicht tragbar ist.)

Prolog hat einen interessanten, großen Vorteil für diese Herausforderung; Sie können eine Funktion schreiben, die mehrere Aufrufmuster verarbeitet (dh Sie können der Funktion nicht nur einen Eingang zuweisen, um einen entsprechenden Ausgang zu erhalten, sondern Sie können der Funktion einen Ausgang zuweisen, um den entsprechenden Eingang zu erhalten). Als solche können wir eine Funktion definieren, die sowohl Komprimierung als auch Dekomprimierung verarbeiten kann. Dies führt zu einer 105-Byte-Übermittlung, die eine Funktion definiert o, die in beide Richtungen funktioniert . (Im Übrigen habe ich es meistens als Dekomprimierer geschrieben - weil es einfacher ist - und den Kompressor "kostenlos" bekommen.) Im Allgemeinen konnten wir in Prolog ein sehr kurzes Programm für diese Aufgabe erwarten, wenn nicht wegen der Tatsache, dass es so schlecht ist bei der Behandlung von Strings (sowohl in Bezug auf fehlende Primitive als auch in Bezug auf die fraglichen Primitive mit furchtbar langen Namen).

Das erste Argument für oist ein Tupel von Zeichenfolgen, z "abcd"-"deab". Das zweite Argument hat eine Form wie "deabcd"-4/6-4/4; Dies ist ein ziemlich normales verschachteltes Tupel und bedeutet, dass die Zeichenfolge "deabcd" ist, die erste Zeichenfolge die Länge 4 hat und am sechsten Zeichen endet, die zweite Zeichenfolge die Länge 4 hat und am vierten Zeichen endet. (Beachten Sie, dass eine Zeichenkette in GNU Prolog nur eine Liste von Zeichencodes ist, was das Debuggen ärgerlich macht, da die Implementierung standardmäßig die letztere Interpretation bevorzugt.) Wenn Sie angebenoWenn Sie ein Argument angeben, wird das andere für Sie ausgegeben (wenn Sie das erste Argument angeben, fungiert dies als Kompressor und wenn Sie das zweite Argument angeben, als Dekomprimierer). Wenn Sie beide Argumente angeben, wird überprüft, ob die komprimierte Darstellung den angegebenen Zeichenfolgen entspricht. Wenn Sie keine Argumente angeben, wird die Ausgabe folgendermaßen generiert:

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

Die obige Beschreibung des E / A-Formats ist so ziemlich nur eine Beschreibung des Programms. Es ist überhaupt nicht viel im Programm. Die einzige Subtilität liegt in den Hinweisen zur Evaluierungsreihenfolge. Wir müssen sicherstellen, dass das Programm nicht nur eine Suchstrategie verwendet, deren Beendigung garantiert ist, sondern auch die kürzestmögliche Ausgabezeichenfolge erzeugt.

Beim Komprimieren beginnen wir mit length(C,_)(" Chat eine Länge"), einem Trick, den ich in vielen Prolog- und Brachylog-Antworten verwendet habe. Wenn dies das erste ist, was Prolog sieht, wird es dazu führen, dass es Prioritäten setzt und die Länge Cüber alles andere reduziert . Dies stellt sicher, dass wir eine Mindestlänge haben C. Die Reihenfolge der Bedingungen in swird sorgfältig ausgewählt, so dass die Suche für jede mögliche Kandidatenlänge von eine begrenzte Zeit in Anspruch nimmt C. Awird eingeschränkt durch C(wir wissen nicht C, aber wir kennen den Zielwert, den wir für seine Länge haben), Mdurch A, Udurch Aund Ldurch U, so dass keine der Suchen unbegrenzte Zeit in Anspruch nehmen kann.

Beim Dekomprimieren werden wir Cdirekt vom Benutzer angegeben. Dies stellt wiederum sicher, dass das Programm aufgrund der gleichen Folge von Einschränkungen in endlicher Zeit ausgeführt wird. (Personen, die die Auswertungsreihenfolge von Prolog kennen, werden feststellen, dass die Definition von sbeim Dekomprimieren sehr ineffizient ist. Das Platzieren length(A,M)und length(U,L)erstens wäre schneller, aber das Bewegen length(A,M)zum Start kann zu einer Endlosschleife beim Komprimieren führen, da sie zu diesem Zeitpunkt weder an etwas gebunden Anoch Man etwas gebunden ist .)


quelle
13

Brachylog , 50 46 Bytes

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

Probieren Sie es online!

Dekomprimieren:

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

Probieren Sie es online!

5 Bytes gespart dank @ ais523

Erläuterung

Die gute Seite deklarativer Sprachen ist, dass wir denselben Code sowohl zum Komprimieren als auch zum Dekomprimieren wiederverwenden können. Daher ist der Code für die Komprimierung genau derselbe wie für die Dekomprimierung , mit einem zusätzlichen Code ~am Anfang.

Dies ~weist Brachylog an, die Reihenfolge der Argumente umzukehren (dh die Eingabe als Ausgabe und die Ausgabe als Eingabe zu verwenden). Da compress keine hat ~, wird das Prädikat in der Standardreihenfolge ausgeführt. Da decompress nur eine hat, wird diese mit der Eingabe als Ausgabe und der Ausgabe als Eingabe ausgeführt.

Auf diese Weise können wir denselben Code (modulo that extra ~) zum Komprimieren und Dekomprimieren verwenden: Komprimieren stellt die beiden Zeichenfolgen als Eingabe und eine Variable als Ausgabe bereit, und Dekomprimieren stellt die Indizes und die komprimierte Zeichenfolge als Ausgabe und eine Variable als Eingabe bereit .

Offensichtlich bedeutet dies auch, dass wir unseren Komprimierungscode etwas explizit beschreiben müssen, damit der Interpreter ihn "rückwärts" ausführen kann. Aus diesem Grund ist der Kompressor selbst etwas lang.

Hier ist eine Aufschlüsselung des Codes für Compress (und damit auch für Decompress):

{……………………………………………………………………}   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]
Tödlich
quelle
4

Jelly , 58 50 Bytes

-1 Byte dank ais523 (Verwendung für einen Zwei-Byte-String)

Dies kann durchaus durchaus golfen ...

Bei der Komprimierung werden zwei Zeichenfolgenargumente verwendet und eine Liste zurückgegeben:
[[[startA, lengthA], [startB, lengthB]], compressedString]

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

Die Dekomprimierung verwendet ein Argument (eine solche Liste) und gibt zwei * Zeichenfolgen zurück:

,
⁾ṫḣżFv
Ḣç€Ṫ

Überlappender Code:

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

Einseitig indiziert.

* Dies ist möglicherweise aufgrund der impliziten Druckformatierung von Jelly nicht offensichtlich. Daher enthält der oben verlinkte Code bei TryItOnline ein zusätzliches Byte (a Yam Ende), um einen Zeilenvorschub zwischen den beiden in die gedruckte Ausgabe einzufügen.

Ich habe mit einem einzelnen Programm begonnen, bei dem die Tiefe des ersten (oder einzigen) Arguments für die Entscheidung zwischen Komprimierung und Dekomprimierung verwendet wurde. Eine nicht verwendete Verknüpfung im Dekomprimierungscode (erste Zeile) und ein einzelnes überlappendes Byte sind jedoch um sieben Byte kürzer .

Wie?

ç;ç@ÑẠ$Ð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.
Jonathan Allan
quelle
“ṫḣ”kann durch Verwendung von Jellys Syntax für Strings mit 2 Zeichen um 1 Byte golfen werden.
Dies ist eine Frage, die nichts mit der eigentlichen Antwort zu tun hat. Schreiben Sie die Erklärung des Codes jedoch von Hand oder gibt es ein Tool, das sie aus Code generiert?
Tfrascaroli
@tfrascaroli Ich schreibe es von Hand
Jonathan Allan