Analizzare il formato del dizionario Bookworm


42

Di recente mi sono lasciato andare a un po 'di nostalgia sotto forma di Bookworm Deluxe:

Nel caso in cui non l'avessi mai visto prima, è un gioco di parole in cui l'obiettivo è quello di collegare tessere adiacenti per formare parole. Per determinare se una stringa è una parola valida, la verifica rispetto al suo dizionario interno, che è memorizzato in un formato compresso che assomiglia a questo:

aa
2h
3ed
ing
s
2l
3iis
s
2rdvark
8s
4wolf
7ves

Le regole per decomprimere il dizionario sono semplici:

  1. Leggi il numero all'inizio della riga e copia quel numero di caratteri dall'inizio della parola precedente. (Se non ci sono numeri, copia quanti più caratteri hai fatto l'ultima volta.)

  2. Aggiungi le seguenti lettere alla parola.

Quindi, la nostra prima parola è aa, seguita da 2h, che significa "copia le prime due lettere di aae append h", formando aah. Quindi 3eddiventa aahed, e poiché la riga successiva non ha un numero, copiamo nuovamente 3 caratteri nel modulo aahing. Questo processo continua per tutto il resto del dizionario. Le parole risultanti dal piccolo input di esempio sono:

aa
aah
aahed
aahing
aahs
aal
aaliis
aals
aardvark
aardvarks
aardwolf
aardwolves

La tua sfida è eseguire questo spacchettamento nel minor numero di byte possibile.

Ogni riga di input conterrà zero o più cifre 0-9 seguite da una o più lettere minuscole a-z. Puoi prendere input e dare output come un elenco di stringhe o come singola stringa con parole separate da qualsiasi carattere diverso da 0-9/ a-z.

Ecco un altro piccolo caso di test con alcuni casi limite non trattati nell'esempio:

abc cba 1de fg hi 0jkl mno abcdefghijk 10l
=> abc cba cde cfg chi jkl mno abcdefghijk abcdefghijl

Puoi anche testare il tuo codice sul dizionario completo: input , output .


Esiste la possibilità che non ci sia un numero nella seconda riga? Inoltre, possiamo supporre che nessun numero tranne 0avrà i primi 0s?
Erik the Outgolfer,

@EriktheOutgolfer Sì, è possibile; L'ho aggiunto al test case. E sì, puoi supporre che (oltre al fatto che il numero non sarà maggiore della lunghezza della parola precedente).
Maniglia della porta

11
Questo è un formato di compressione carino:]
Poke

1
Il locateprogramma utilizza questo tipo di codifica sui nomi dei percorsi.
Dan D.

Ho scritto questo programma per il mio effettivo utilizzo, circa 15 anni fa. Sfortunatamente non penso di avere più la fonte ...
Hobbs,

Risposte:



10

JavaScript (ES6),  66 62  61 byte

a=>a.map(p=s=>a=a.slice([,x,y]=/(\d*)(.*)/.exec(s),p=x||p)+y)

Provalo online!

Commentate

a =>                  // a[] = input, re-used to store the previous word
  a.map(p =           // initialize p to a non-numeric value
  s =>                // for each string s in a[]:
    a =               //   update a:
      a.slice(        //     extract the correct prefix from the previous word:
        [, x, y] =    //       load into x and y:
          /(\d*)(.*)/ //         the result of a regular expression which splits the new
          .exec(s),   //         entry into x = leading digits and y = trailing letters
                      //       this array is interpreted as 0 by slice()
        p = x || p    //       update p to x if x is not an empty string; otherwise leave
                      //       it unchanged; use this as the 2nd parameter of slice()
      )               //     end of slice()
      + y             //     append the new suffix
  )                   // end of map()

5

Perl 6 , 50 48 byte

-2 byte grazie a nwellnhof

{my$l;.map:{$!=S[\d*]=substr $!,0,$l [R||]=~$/}}

Provalo online!

Un porto della soluzione di Arnauld . Amico, quel R||trucco era una montagna russa da "Penso che questo potrebbe essere possibile", a "nah, è impossibile", a "un po 'forse possibile" e infine "aha!"

Spiegazione:

{my$l;.map:{$!=S[\d*]=substr $!,0,$l [R||]=~$/}}
{                                              }  # Anonymous code block
 my$l;    # Declare the variable $l, which is used for the previous number
      .map:{                                  }  # Map the input list to
            $!=              # $! is used to save the previous word
               S[\d*]=       # Substitute the number for
                      substr $!,0    # A substring of the previous word
                                 ,              # With the length of 
                                           ~$0     # The num if it exists
                                  $l [R||]=        # Otherwise the previous num

La $l [R||]=~$/parte si traduce approssimativamente in $l= ~$/||+$lma ... ha la stessa quantità di byte :(. Inizialmente, salvava i byte utilizzando una variabile anonima, quindi non my$lc'era più, ma non funziona poiché l'ambito è ora la sostituzione, non il mapblocco del codice. Oh bene. Comunque, Rè il metaoperatore inverso, quindi inverte gli argomenti di ||, quindi alla $lvariabile viene assegnato il nuovo numero ( ~$/) se esiste, altrimenti di nuovo se stesso.

Potrebbe essere 47 byte se Perl 6 non generasse un errore di compilatore ridondante =~.


5

Rubino , 49 45 43 byte

$0=$_=$0[/.{0#{p=$_[/\d+/]||p}}/]+$_[/\D+/]

Provalo online!

Spiegazione

$0=                                         #Previous word, assign the value of
   $_=                                      #Current word, assign the value of
      $0[/.{0#{              }}/]           #Starting substring of $0 of length p which is
               p=$_[/\d+/]||p               #defined as a number in the start of $_ if any 
                                 +$_[/\D+/] #Plus any remaining non-digits in $_

5

C, 65 57 byte

n;f(){char c[99];while(scanf("%d",&n),gets(c+n))puts(c);}

Provalo online!

Spiegazione:

n;                     /* n is implicitly int, and initialized to zero. */

f() {                  /* the unpacking function. */

    char c[99];        /* we need a buffer to read into, for the longest line in
                          the full dictionary we need 12 + 1 bytes. */

    while(             /* loop while there is input left. */

        scanf("%d",&n) /* Read into n, if the read fails because this line
                          doesn't have a number n's value does not change.
                          scanf's return value is ignored. */

        ,              /* chain expressions with the comma operator. The loop
                          condition is on the right side of the comma. */

        gets(c+n))     /* we read into c starting from cₙ. c₀, c₁.. up to cₙ is
                          the shared prefix of the word we are reading and the
                          previous word. When gets is successful it returns c+n
                          else it will return NULL. When the loop condition is
                          NULL the loop exits. */

        puts(c);}      /* print the unpacked word. */

5

Brainfuck , 201 byte

,[[[-<+>>>+<<]>-[---<+>]<[[-<]>>]<[-]>>[<<,>>>[-[-<++++++++++>]]++++<[->+<]-[----->-<]<]<]>>>[[>>]+[-<<]>>[[>>]+[<<]>>-]]+[>>]<[-]<[<<]>[->[>>]<+<[<<]>]>[>.>]+[>[-]<,.[->+>+<<]>>----------]<[<<]>-<<<,]

Provalo online!

Richiede una nuova riga finale alla fine dell'input. Una versione senza questo requisito è più lunga di 6 byte:

Brainfuck , 207 byte

,[[[-<+>>>+<<]>-[---<+>]<[[-<]>>]<[-]>>[<<,>>>[-[-<++++++++++>]]++++<[->+<]-[----->-<]<]<]>>>[[>>]+[-<<]>>[[>>]+[<<]>>-]]+[>>]<[-]<[<<]>[->[>>]<+<[<<]>]>[>.>]+[>[-]<,[->+>+<<]>>[----------<.<]>>]<[<<]>-<<<,]

Provalo online!

Entrambe le versioni presuppongono che tutti i numeri siano rigorosamente inferiori a 255.

Spiegazione

Il nastro è strutturato come segue:

tempinputcopy 85 0 inputcopy number 1 a 1 a 1 r 1 d 0 w 0 o 0 l 0 f 0 ...

La cella "numero" è uguale a 0 se non vengono immesse cifre e n + 1 se viene inserito il numero n. L'immissione viene effettuata nella cella contrassegnata con "85".

,[                     take input and start main loop
 [                     start number input loop
  [-<+>>>+<<]          copy input to tempinputcopy and inputcopy
  >-[---<+>]           put the number 85 in the cell where input was taken
  <[[-<]>>]            test whether input is less than 85; ending position depends on result of comparison
                       (note that digits are 48 through 57 while letters are 97 through 122)
  <[-]>                clean up by zeroing out the cell that didn't already become zero
  >[                   if input was a digit:
   <<,>>               get next input character
   >[-[-<++++++++++>]] multiply current value by 10 and add to current input
   ++++                set number cell to 4 (as part of subtracting 47)
   <[->+<]             add input plus 10*number back to number cell
   -[----->-<]         subtract 51
  <]                   move to cell we would be at if input were a letter
 <]                    move to input cell; this is occupied iff input was a digit

                       part 2: update/output word

 >>>                   move to number cell
 [                     if occupied (number was input):
  [>>]+[-<<]>>         remove existing marker 1s and decrement number cell to true value
  [[>>]+[<<]>>-]       create the correct amount of marker 1s
 ]
 +[>>]<[-]             zero out cell containing next letter from previous word
 <[<<]>                return to inputcopy
 [->[>>]<+<[<<]>]      move input copy to next letter cell
 >[>.>]                output word so far
 +[                    do until newline is read:
  >[-]<                zero out letter cell
  ,.                   input and output next letter or newline
  [->+>+<<]            copy to letter cell and following cell
  >>----------         subtract 10 to compare to newline
 ]
 <[<<]>-               zero out number cell (which was 1 to make copy loop shorter)
 <<<,                  return to input cell and take input
]                      repeat until end of input

4

Python 3.6+, 172 195 156 123 122 121 104 byte

import re
def f(l,n=0,w=""):
 for s in l:t=re.match("\d*",s)[0];n=int(t or n);w=w[:n]+s[len(t):];yield w

Provalo online!

Spiegazione

Ho ceduto e usato espressioni regolari. Ciò ha salvato almeno 17 byte. :

t=re.match("\d*",s)[0]

Quando la stringa non inizia affatto con una cifra, la lunghezza di questa stringa sarà 0. Ciò significa che:

n=int(t or n)

sarà nse tè vuoto, e int(t)altrimenti.

w=w[:n]+s[len(t):]

rimuove il numero da cui è stata trovata l'espressione regolare s(se non viene trovato alcun numero, rimuoverà i 0caratteri, lasciando non stroncato) e sostituisce tutti i ncaratteri tranne la prima della parola precedente con il frammento di parola corrente; e:

yield w

genera la parola corrente.


4

Haskell, 82 81 byte

tail.map concat.scanl p["",""]
p[n,l]a|[(i,r)]<-reads a=[take i$n++l,r]|1<2=[n,a]

Prende e restituisce un elenco di stringhe.

Provalo online!

        scanl p["",""]        -- fold function 'p' into the input list starting with
                              -- a list of two empty strings and collect the
                              -- intermediate results in a list
  p [n,l] a                   -- 1st string of the list 'n' is the part taken form the last word
                              -- 2nd string of the list 'l' is the part from the current line
                              -- 'a' is the code from the next line
     |[(i,r)]<-reads a        -- if 'a' can be parsed as an integer 'i' and a string 'r'
       =[take i$n++l,r]       -- go on with the first 'i' chars from the last line (-> 'n' and 'l' concatenated) and the new ending 'r'
     |1<2                     -- if parsing is not possible
       =[n,a]                 -- go on with the previous beginning of the word 'n' and the new end 'a'
                              -- e.g. [         "aa",     "2h",      "3ed",       "ing"       ] 
                              -- ->   [["",""],["","aa"],["aa","h"],["aah","ed"],["aah","ing"]]
  map concat                  -- concatenate each sublist
tail                          -- drop first element. 'scanl' saves the initial value in the list of intermediate results. 

Modifica: -1 byte grazie a @Nitrodon.


1
Contrariamente alla solita saggezza del golf di Haskell, puoi effettivamente salvare un byte qui non definendo la funzione di aiuto come operatore infix.
Nitrodon,

@Nitrodon: ben individuato! Grazie!
nimi,

3

Japt, 19 18 17 byte

Inizialmente ispirato alla soluzione JS di Arnauld .

;£=¯V=XkB ªV +XoB

Provalo

                      :Implicit input of string array U
 £                    :Map each X
   ¯                  :  Slice U to index
      Xk              :    Remove from X
;       B             :     The lowercase alphabet (leaving only the digits or an empty string, which is falsey)
          ªV          :    Logical OR with V (initially 0)
    V=                :    Assign the result to V for the next iteration
             +        :  Append
              Xo      :  Remove everything from X, except
;               B     :   The lowercase alphabet
  =                   :  Reassign the resulting string to U for the next iteration

2

Gelatina , 16 byte

⁹fØDVo©®⁸ḣ;ḟØDµ\

Provalo online!

Come funziona

⁹fØDVo©®⁸ḣ;ḟØDµ\  Main link. Argument: A (array of strings)

              µ\  Cumulatively reduce A by the link to the left.
⁹                     Yield the right argument.
  ØD                  Yield "0123456789".
 f                    Filter; keep only digits.
    V                 Eval the result. An empty string yields 0.
     o©               Perform logical OR and copy the result to the register.
       ®              Yield the value in the register (initially 0).
        ⁸ḣ            Head; keep that many character of the left argument.
          ;           Concatenate the result and the right argument.
            ØD        Yield "0123456789".
           ḟ          Filterfalse; keep only non-digits.


1

Retina 0.8.2 , 69 byte

+`((\d+).*¶)(\D)
$1$2$3
\d+
$*
+m`^((.)*(.).*¶(?<-2>.)*)(?(2)$)1
$1$3

Provalo online! Il collegamento include casi di test più difficili. Spiegazione:

+`((\d+).*¶)(\D)
$1$2$3

Per tutte le righe che iniziano con le lettere, copia il numero dalla riga precedente, eseguendo il ciclo fino a quando tutte le righe iniziano con un numero.

\d+
$*

Convertire il numero in unario.

+m`^((.)*(.).*¶(?<-2>.)*)(?(2)$)1
$1$3

Utilizzare i gruppi di bilanciamento per sostituire tutte le 1s con la lettera corrispondente della riga precedente. (Questo risulta essere leggermente più golfoso rispetto alla sostituzione di tutte le corse di 1s.)


1

Rosso , 143 byte

func[b][a: charset[#"a"-#"z"]u: b/1 n: 0 foreach c b[parse c[copy m to a
p: copy s to end(if p<> c[n: do m]print u: rejoin[copy/part u n s])]]]

Provalo online!



1

Groovy , 74 byte

{w="";d=0;it.replaceAll(/(\d*)(.+)/){d=(it[1]?:d)as int;w=w[0..<d]+it[2]}}

Provalo online!

Spiegazione:

{                                                                        }  Closure, sole argument = it
 w="";d=0;                                                                  Initialize variables
          it.replaceAll(/(\d*)(.+)/){                                   }   Replace every line (since this matches every line) and implicitly return. Loop variable is again it
                                     d=(it[1]?:d)as int;                    If a number is matched, set d to the number as an integer, else keep the value
                                                        w=w[0..<d]+it[2]    Set w to the first d characters of w, plus the matched string


0

Perl 5 -p , 45 41 byte

s:\d*:substr($p,0,$l=$&+$l*/^\D/):e;$p=$_

Provalo online!

Spiegazione:

s:\d*:substr($p,0,$l=$&+$l*/^\D/):e;$p=$_ Full program, implicit input
s:   :                           :e;      Replace
  \d*                                       Any number of digits
      substr($p,0,              )           By a prefix of $p (previous result or "")
                  $l=  +                      With a length (assigned to $l) of the sum
                     $&                         of the matched digits
                          *                     and the product
                        $l                        of $l (previous length or 0)
                           /^\D/                  and whether there is no number in the beginning (1 or 0)
                                                (product is $l if no number)
                                    $p=$_ Assign output to $p
                                          Implicit output


0

05AB1E , 20 19 17 byte

õUvyþDõÊi£U}Xyá«=

Provalo online o verifica tutti i casi di test .

Spiegazione:

õ                  # Push an empty string ""
 U                 # Pop and store it in variable `X`
v                  # Loop `y` over the (implicit) input-list
 yþ                #  Push `y`, and leave only the digits (let's call it `n`)
   DõÊi  }         #  If it's NOT equal to an empty string "":
       £           #   Pop and push the first `n` characters of the string
        U          #   Pop and store it in variable `X`
          X        #  Push variable `X`
           yá      #  Push `y`, and leave only the letters
             «     #  Merge them together
              =    #  Print it (without popping)

0

Lisp comune, 181 byte

(do(w(p 0))((not(setf g(read-line t()))))(multiple-value-bind(a b)(parse-integer g :junk-allowed t)(setf p(or a p)w(concatenate'string(subseq w 0 p)(subseq g b)))(format t"~a~%"w)))

Provalo online!

Ungolfed:

(do (w (p 0))   ; w previous word, p previous integer prefix (initialized to 0)
    ((not (setf g (read-line t ()))))   ; read a line into new variable g
                                        ; and if null terminate: 
  (multiple-value-bind (a b)            ; let a, b the current integer prefix
      (parse-integer g :junk-allowed t) ; and the position after the prefix
    (setf p (or a p)                    ; set p to a (if nil (no numeric prefix) to 0)
          w (concatenate 'string        ; set w to the concatenation of prefix
             (subseq w 0 p)             ; characters from the previous word 
             (subseq g b)))             ; and the rest of the current line
    (format t"~a~%"w)))                 ; print the current word

Come al solito, i lunghi identificatori di Common Lisp lo rendono non particolarmente adatto per PPCG.



0

C # (compilatore interattivo Visual C #) , 134 byte

a=>{int l=0,m,n;var p="";return a.Select(s=>{for(m=n=0;s[m]<58;n=n*10+s[m++]-48);return p=p.Substring(0,l=m>0?n:l)+s.Substring(m);});}

Provalo online!

-9 byte grazie a @ASCIIOnly!

Meno golf ...

// a is an input list of strings
a=>{
  // l: last prefix length
  // m: current number of digits
  // n: current prefix length
  int l=0,m,n;
  // previous word
  var p="";
  // run a LINQ select against the input
  // s is the current word
  return a.Select(s=>{
    // nibble digits from start of the
    // current word to build up the
    // current prefix length
    for(m=n=0;
      s[m]<58;
      n=n*10+s[m++]-48);
    // append the prefix from the
    // previous word to the current
    // word and capture values
    // for the next iteration
    return
      p=p.Substring(0,l=m>0?n:l)+
      s.Substring(m);
  });
}


Questo è abbastanza cool :) ho cambiato l=n>0?n:lper l=m>0?n:lperché non stava raccogliendo il caso in cui una linea iniziata con zero ( 0jkl). Grazie per il consiglio!
dana,

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.