Analizar el formato del diccionario Bookworm


42

Recientemente me he dado un poco de nostalgia en forma de Bookworm Deluxe:

En caso de que no lo hayas visto antes, es un juego de palabras donde el objetivo es conectar fichas adyacentes para formar palabras. Para determinar si una cadena es una palabra válida, la compara con su diccionario interno, que se almacena en un formato comprimido que se ve así:

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

Las reglas para desempacar el diccionario son simples:

  1. Lea el número al comienzo de la línea y copie esa cantidad de caracteres desde el comienzo de la palabra anterior. (Si no hay un número, copie tantos caracteres como lo hizo la última vez).

  2. Agregue las siguientes letras a la palabra.

Entonces, nuestra primera palabra es aa, seguida de 2h, que significa "copiar las dos primeras letras de aay agregar h", formando aah. Luego se 3edconvierte aahed, y como la siguiente línea no tiene un número, copiamos 3 caracteres nuevamente para formar aahing. Este proceso continúa durante el resto del diccionario. Las palabras resultantes de la entrada de muestra pequeña son:

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

Su desafío es realizar este desempaquetado en la menor cantidad de bytes posible.

Cada línea de entrada contendrá cero o más dígitos 0-9 seguidos de una o más letras minúsculas a-z. Puede tomar entrada y dar salida como una lista de cadenas o como una sola cadena con palabras separadas por cualquier carácter que no sea 0-9/ a-z.

Aquí hay otro pequeño caso de prueba con algunos casos extremos no cubiertos en el ejemplo:

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

También puede probar su código en el diccionario completo: entrada , salida .


¿Existe la posibilidad de que no haya un número en la segunda línea? Además, ¿podemos suponer que ningún número excepto 0tendrá 0s iniciales ?
Erik the Outgolfer

@EriktheOutgolfer Sí, eso es posible; Lo agregué al caso de prueba. Y sí, puede suponer eso (además de que el número no será mayor que la longitud de la palabra anterior).
Pomo de la puerta

11
Ese es un lindo formato de compresión:]
Poke

1
El locateprograma usa este tipo de codificación en los nombres de ruta.
Dan D.

Escribí este programa para mi uso real, hace unos 15 años. Desafortunadamente, ya no creo tener la fuente ...
hobbs

Respuestas:


13

Vim, 57 bytes

:%s/\a/ &
:%norm +hkyiwjP
:g/\d/norm diw-@"yl+P
:%s/ //g

Pruébalo en línea!


¿Funcionaría en <H<Glugar de la última sustitución?
Kritixi Lithos

@cowsquack Lamentablemente, no. Cada entrada que no comienza con un número aumenta el número de espacios iniciales, por lo que no hay forma de garantizar que una <solución sea indeseable suficientes veces.
DJMcMayhem

Creo que puede hacer en :%s/ *lugar de la última sustitución para guardar dos bytes.
Dexter CD

10

JavaScript (ES6),  66 62  61 bytes

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

Pruébalo en línea!

Comentado

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 bytes

-2 bytes gracias a nwellnhof

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

Pruébalo en línea!

Un puerto de la solución de Arnauld . Hombre, esoR|| truco fue una montaña rusa de 'Creo que esto podría ser posible', 'nah, es imposible', 'quizás un poco posible' y finalmente '¡ajá!'

Explicación:

{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 se traduce aproximadamente como $l= ~$/||+$l... pero tiene la misma cantidad de bytes :(. Originalmente, guardaba bytes usando una variable anónima, por my$llo que desapareció pero eso no funciona ya que el alcance ahora es la sustitución, no el mapbloque de código. Oh bien. De todos modos, Res el metaoperador inverso, por lo que invierte los argumentos de ||, por lo que a la $lvariable se le asigna el nuevo número (~$/ ) si existe, de lo contrario se vuelve a .

Podría ser de 47 bytes si Perl 6 no arrojó un error de compilación algo redundante =~.


5

Ruby , 49 45 43 bytes

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

Pruébalo en línea!

Explicación

$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 bytes

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

Pruébalo en línea!

Explicación:

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 bytes

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

Pruébalo en línea!

Requiere una nueva línea al final de la entrada. Una versión sin este requisito es 6 bytes más larga:

brainfuck , 207 bytes

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

Pruébalo en línea!

Ambas versiones suponen que todos los números son estrictamente menores que 255.

Explicación

La cinta se presenta de la siguiente manera:

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

La celda "número" es igual a 0 si no se ingresan dígitos, y n + 1 si se ingresa el número n. La entrada se toma en la celda marcada "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 bytes

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

Pruébalo en línea!

Explicación

Me derrumbé y usé expresiones regulares. Esto ahorró al menos 17 bytes. :

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

Cuando la cadena no comienza con un dígito, la longitud de esta cadena será 0. Esto significa que:

n=int(t or n)

será nsi testá vacío, y de lo int(t)contrario.

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

elimina el número del que se encontró la expresión regular s(si no se encuentra un número, eliminará los 0caracteres, dejándolos sin struncar) y reemplaza todos, excepto los primeros ncaracteres de la palabra anterior, con el fragmento de palabra actual; y:

yield w

da salida a la palabra actual.


4

Haskell, 82 81 bytes

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

Toma y devuelve una lista de cadenas.

Pruébalo en línea!

        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. 

Editar: -1 byte gracias a @Nitrodon.


1
Contrariamente a la sabiduría habitual de golf de Haskell, en realidad puede guardar un byte aquí al no definir la función auxiliar como un operador infijo.
Nitrodon el

@Nitrodon: bien visto! ¡Gracias!
nimi

3

Japt, 19 18 17 bytes

Inicialmente inspirado en la solución JS de Arnauld .

;£=¯V=XkB ªV +XoB

Intentalo

                      :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

Jalea , 16 bytes

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

Pruébalo en línea!

Cómo funciona

⁹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 bytes

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

Pruébalo en línea! Link incluye casos de prueba más difíciles. Explicación:

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

Para todas las líneas que comienzan con letras, copie el número de la línea anterior, haciendo un bucle hasta que todas las líneas comiencen con un número.

\d+
$*

Convierte el número a unario.

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

Use grupos de equilibrio para reemplazar todos los 1s con la letra correspondiente de la línea anterior. (Esto resulta ser un poco más golfístico que reemplazar todas las carreras de 1s.)




1

Groovy , 74 bytes

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

Pruébalo en línea!

Explicación:

{                                                                        }  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 bytes

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

Pruébalo en línea!

Explicación:

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 bytes

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

Pruébelo en línea o verifique todos los casos de prueba .

Explicación:

õ                  # 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 común, 181 bytes

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

Pruébalo en línea!

Sin golf:

(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

Como de costumbre, los identificadores largos de Common Lisp lo hacen no particularmente adecuado para PPCG.



0

C # (compilador interactivo de Visual C #) , 134 bytes

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);});}

Pruébalo en línea!

-9 bytes gracias a @ASCIIOnly!

Menos 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);
  });
}


Eso es genial :) Cambié l=n>0?n:la l=m>0?n:lporque no estaba recogiendo el caso cuando una línea comenzó con cero ( 0jkl). ¡Gracias por el consejo!
dana

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.