Haga que esta explicación de código sea bonita nuevamente

17

Introducción

La mayoría de los golfistas de código aquí agregan explicaciones a sus presentaciones, por lo que es más fácil entender lo que está sucediendo. Por lo general, las líneas de código van a la izquierda y la explicación correspondiente a la derecha con algún tipo de separador. Para que se vea bonito, los separadores están todos en la misma columna. Además, el texto de explicación largo generalmente se ajusta a la siguiente línea, por lo que los lectores no tienen que desplazarse horizontalmente para leer todo.

Sin embargo, cuando quieres editar esta explicación porque hiciste algunos campos de golf locos, a menudo terminas pasando tiempo para hacer que tu explicación vuelva a ser bonita. Dado que esta es una tarea muy repetitiva, desea escribir un programa para esto.

El reto

Dadas varias líneas de código con explicación y un separador, genera el código bien formateado con explicación.

Ejemplo

Entrada

shM-crz1dc4. "ANDBYOROF # z = input

     rz1 # convierte la entrada a mayúsculas
    cd # entrada dividida en espacios
         c4. "ANDBYOROF # crea una lista de las palabras de una cadena empaquetada que debe ignorarse
   - # filtra esas palabras
 hM # solo toma la primera letra de todas las palabras
s # únelos en una cadena

Salida

shM-crz1dc4. "ANDBYOROF # z = input

     rz1 # convierte la entrada a mayúsculas
    cd # entrada dividida en espacios
         c4. "ANDBYOROF # crea una lista de las palabras de una cadena empaquetada que será
                           # ignorado
   - # filtra esas palabras
 hM # solo toma la primera letra de todas las palabras
s # únelos en una cadena

Una cookie para el primero que puede descubrir qué hace este código.

El algoritmo de formateo

  • Encuentre la línea de código más larga (excluyendo la explicación y los espacios entre el código y el separador).
  • Agregue 5 espacios después de esta línea de código y agregue el separador correspondiente con explicación. Esta es ahora la línea de referencia.
  • Ajuste cada dos líneas a esta línea de referencia, de modo que los separadores estén todos en la misma columna.
  • Ajuste todas las líneas que tengan más de 93 caracteres en una nueva línea de la siguiente manera:
    • Encuentre la última palabra cuyo final está en la columna 93 o inferior.
    • Tome todas las palabras después de esta y envuélvalas a una nueva línea con el separador principal y el espacio correcto. El espacio entre esas dos palabras debe eliminarse, por lo que la primera línea termina con un carácter de palabra y la segunda línea comienza con una después del separador.
    • Si la línea resultante aún tiene más de 93 caracteres, haga lo mismo nuevamente hasta que cada línea tenga menos de 94 caracteres.

Notas

  • Una palabra consta de caracteres que no son espacios en blanco. Las palabras están separadas por un solo espacio.
  • La palabra envoltura siempre es posible. Esto significa que ninguna palabra es tan larga que haría imposible el ajuste.
  • La entrada solo contendrá ASCII imprimible y no tendrá espacios en blanco al final
  • El separador solo aparecerá una vez por línea.
  • Si bien la explicación puede tener una longitud ilimitada, el separador y el código solo pueden tener una longitud máxima combinada de 93 - 5 = 87caracteres. Los 5 caracteres son los espacios entre el código y el separador. El código y el separador siempre tendrán al menos un carácter.
  • La entrada puede contener líneas vacías. Esos nunca contendrán ningún carácter (excepto una nueva línea si toma la entrada como cadena multilínea). Esas líneas vacías también deben estar presentes en la salida.
  • Cada línea tendrá un código, un separador y una explicación. Las excepciones son líneas vacías.
  • Puede tomar la entrada en cualquier formato razonable, siempre que no se procese previamente. Deje claro en su respuesta cuál usa.
  • La salida puede ser una cadena multilínea o una lista de cadenas.

Reglas

Casos de prueba

El formato de entrada aquí es una lista de cadenas que representan las líneas y una sola cadena para el separador. Ambos están separados por una coma. La salida es una lista de cadenas.

['shM-crz1dc4. "ANDBYOROF # z = input', '', 'rz1 # convierte la entrada a mayúsculas', 'cd # divide la entrada en espacios', 'c4." ANDBYOROF # crea una lista de las palabras de un paquete cadena que se ignorará ',' - # filtra esas palabras ',' hM # solo toma la primera letra de todas las palabras ',' s # únelas en una cadena '], "#" -> [' shM-crz1dc4 . "ANDBYOROF # z = input ',' ',' rz1 # convierte la entrada a mayúsculas ',' cd # divide la entrada en espacios ',' c4." ANDBYOROF # crea una lista de las palabras de una cadena empaquetada que será ' , '# ignorado', '- # filtra esas palabras ',' hM # solo toma la primera letra de todas las palabras ',' s # únelas en una cadena ']
['codecodecode e # Explicación', 'sdf dsf sdf e # A Muy muy muy muy muy muy muy muy muy largo largo largo largo largo largo largo largo largo largo largo explicación larga y larga y larga', '', 'algunos más codee # y alguna explicación más '], "e #" -> [' codecodecode e # Explicación ',' sdf dsf sdf e # A Muy muy muy muy muy muy muy muy muy largo largo largo largo largo largo ',' e # largo larga, larga, larga, larga, larga explicación y se vuelve más larga ',' e # y más larga ',' ',' algo más de código e # y alguna explicación más ']

¡Feliz codificación!

Denker
fuente
1
@ Matt Todos los separadores están siempre en la columna length of the longest code-line + 5. Esto también se aplica a las líneas que solo contienen una explicación, ya que fueron ajustadas.
Denker
Dios mío, he estado haciendo esto mal durante las últimas 3 horas. Estaba tratando de envolver un código largo y dejar las explicaciones largas ... Bueno, comenzando de nuevo. Al menos ahora es más fácil. Gracias. Lo redactaste bien ... Solo soy tonto.
Matt
Ajustar todas las líneas que tengan más de 93 caracteres ¿Eso significa que el código, incluidos los espacios iniciales, nunca tendrá más de 87 caracteres?
Matt
@Matt El código y el separador juntos nunca tendrán más de 87 caracteres, ya que necesitamos 5 espacios entre el código y el separador y un carácter para la explicación.
Denker
1
El código Pyth encuentra la abreviatura de cualquier cadena dada. Lo sabría porque esa fue una respuesta a mi pregunta.
Aplet123

Respuestas:

3

Ruby, 245 237 220 216 212 209 205 bytes

Función anónima. Enfoque bastante básico (encuentre la longitud máxima, agregue 5, luego procese en cada línea, con recursión para lidiar con el ajuste) y puede haber otro enfoque que ahorre más bytes.

Eliminé la respuesta anterior que no cumplía con todos los requisitos; No quería tener un código de media respuesta como respuesta (también estaba recibiendo votos negativos por estar incompleto), pero ahora debería hacer todo lo que la pregunta me pide.

->x,d{l,S=0," "
s=->n{m,q=n.size,94-l-d.size
m>q ?(i=n.rindex(S,q)
n[0,i]+"
"+S*l+d+s[n[i+1,m]]):n}
x.map{|n|c,t=n.split d
c=(c||S).rstrip
l=[l,5+c.size].max
[c,t]}.map{|c,t|c+S*(l-c.size)+d+s[t]if t}*"
"}

Registro de cambios:

  • Ahorró algunos bytes al aprovechar algunas de las promesas en la entrada, especialmente la promesa de que todas las líneas no vacías tienen el carácter separador y una explicación.
  • Logré jugar un poco más al golf al guardar las cadenas divididas desde la primera mapllamada y eliminar algunas stripfunciones innecesarias basadas en la promesa de que las palabras en la explicación siempre tienen exactamente un espacio entre ellas. También," " está asignado a una constante ya que lo uso mucho.
  • Encadenó ambas mapllamadas al aprovechar el poder de las funciones de orden superior, lo que significa que la primera llamada del mapa establecerá la variable de longitud lcorrectamente incluso si se llama después de la declaración de la función auxiliar s. -4 bytes.
  • Cuerdas multilínea abusadas para reemplazar \n líneas múltiples abusadas con nuevas líneas reales, más un pequeño truco usando ifoperadores ternarios (cuando joinse llama en una matriz con nilvalores, se convierten en cadenas vacías)
  • .joinaparentemente puede ser reemplazado por a *.
Tinta de valor
fuente
Creo que debería arreglarse ahora?
Value Ink
¿Cómo se ajusta esto a 94?
Ven
Muy bien, ahora que tenía más tiempo para trabajar en el código, se ajusta correctamente.
Value Ink
"Si bien la explicación puede tener una longitud ilimitada, el separador y el código solo pueden tener una longitud máxima combinada de 93 - 5 = 87caracteres. Los 5 caracteres son los espacios entre el código y el separador. El código y el separador siempre tendrán al menos un carácter de largo". Su sección de código está más allá del límite, con 97 caracteres, por lo que el programa tiene un comportamiento indefinido.
Value Ink
ah, bien visto, tiene sentido!
Ven
9

LiveScript, 243 236 233 228 219 225 bytes

f = (x,k,m+5)->l=(.length);x.=map(->it/"#k"=>..0-=/ +$/;m>?=5+l ..0);i=0;[..0&&..0+' '*(m- l ..0)+k+..1 for x]=>while i<(l ..),++i=>j=(s=..[i])lastIndexOf ' ' 93;(..splice i+1 0 ' '*m+k+s[j to]*'';s.=substr 0 j) if 94<l s;..[i]=s

Cómo funciona: principalmente como el código Java. Comience por aliasing length (LiveScript permite crear una función a partir de operadores utilizando paréntesis). .=es a = a.b- que usamos aquí para mapear.

=> blabla ..es la construcción de cascada Smalltalk-ish: el lado izquierdo =>es accesible como ..para el resto del bloque; Y será devuelto. Aquí, es el elemento dividido en k. Nota: Estoy usando la interpolación de cadenas, porque /solo significa "dividir" con una cadena literal.

LS también nos permite usar a-=/regexp/en este lambda (también funciona con literales de cadena): es solo azúcar para una .replacellamada.

Finalmente, el >?=es el >?operador combinatorio -assin, que devuelve el mayor de dos operandos.

LS tiene estilo Python / Haskell para comprensión, sin nada sofisticado, excepto la "cadena * veces" para repetir el espacio lo suficiente.

Esto para la comprensión sirve como tema (ver el bloque sobre las cascadas de la otra).

Luego hacemos un bucle en cada elemento de la matriz (el que acabamos de construir con la comprensión), y si alguna línea es mayor que 93 caracteres, encontramos el último índice de, dividido allí, y empujamos la línea separada justo después de esta iteración actual ( ... para que la próxima iteración se vuelva a dividir si la línea es demasiado grande).

Lo último que le apetece a[j to]es un rango (desde j hasta el final), pero dado que usa métodos de matriz, tenemos que volver a unirlo a una cadena, lo que hacemos usando sobrecargado *:*'' .

ejemplo

s = """this is kod # Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
d # y

efgh # z"""

f = (x,k,m=5)->l=(.length);x.=map(->it/"#k"=>..0-=/ +$/;m>?=5+l ..0);i=0;[..0&&..0+' '*(m- l ..0)+k+..1 for x]=>while i<(l ..),++i=>j=(s=..[i])lastIndexOf ' ' 93;(..splice i+1 0 ' '*m+k+s[j to]*'';s.=substr 0 j) if 94<l s;..[i]=s

console.log (f s / '\n', '#') * \\n

salida:

this is kod     # Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
                # tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
                # veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
                # commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
                # velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
                # cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
                # est laborum.
d               # y

efgh            # z
Ven
fuente
1
a quien votó en contra: la respuesta es fija.
Ven
2
Cuando se desborda una explicación, necesita las nuevas líneas para alinear sus caracteres separadores con el resto, IIRC.
Value Ink
@KevinLau bien visto, arreglado!
Ven
¿Puedes actualizar tu salida de ejemplo también?
Value Ink
@KevinLau hecho.
Ven
6

Java, 347 + 19 = 366 bytes

Requiere

import java.util.*;

Así los +19 bytes.

(c,s)->{int p=0,i=0,t;String l;for(;i<c.size();i++){l=c.get(i);l=l.replaceAll(" *"+s,s);p=Math.max(l.indexOf(s),p);c.set(i,l);}p+=5;for(i=0;i<c.size();i++){l=c.get(i);t=l.indexOf(s);while(t>-1&t<p)l=l.substring(0,t)+" "+l.substring(t++);t=93;if(l.length()>t){while(l.charAt(t)!=' ')t--;c.add(i+1,s+l.substring(t));l=l.substring(0,t);}c.set(i,l);}}

Toma en el formato f.accept(List<String> code, String seperator). Formatos en el lugar. Una versión que crea y devuelve una nueva List<String>sería trivial de implementar pero costaría algunos bytes.

Sangrado + ejemplo de uso:

static BiConsumer<List<String>, String> prettify = (code, seperator) -> {
    int space = 0, i=0, t;
    String line;
    for (; i<code.size(); i++) { // for each line
        line = code.get(i); // get line
        line = line.replaceAll(" *" + seperator, seperator); // strip space before seperator
        space = Math.max(line.indexOf(seperator), space); // save biggest space until seperator
        code.set(i, line); // save line
    }
    space += 5;
    for (i=0; i<code.size(); i++) { // for each line
        line = code.get(i); // get line
        t = line.indexOf(seperator); // get index of seperator
        while (t>-1&t<space) // while the seperator exists and is further left than desired
            line = line.substring(0,t) + " " + line.substring(t++); // move it right by adding a space before it
        t = 93; // get desired line length
        if (line.length()>t) { // if the line is longer than that
            while (line.charAt(t)!=' ') t--; // scan backwards for a space
            code.add(i+1, seperator + line.substring(t)); // add a line after this one with seperator and the rest of the line
                                                          // the next pass will space it correctly
            line = line.substring(0,t); // cut off this line at that point
        }
        code.set(i, line); // save edited line back to List
    }
};

public static void main(String[] args) {
    List<String> code = new ArrayList<>();
    code.add("shM-crz1dc4.\"ANDBYOROF  # z = input");
    code.add("");
    code.add("     rz1      # convert input to uppercase");
    code.add("    c   d        # split input on spaces");
    code.add("         c4.\"ANDBYOROF        # create a list of the words from a packed string which shall be ignored");
    code.add("   -          # filter those words out");
    code.add(" hM                # only take the first letter of all words");
    code.add("s                   # join them into one string");
    prettify.accept(code, "#");
    code.stream().forEach(System.out::println);
}

... probablemente debería ejecutar esto por sí mismo: P

CAD97
fuente
Si alguien puede entender por qué replace(" *"+s)no funciona, pero replaceAll(" *"+s)me encantaría escucharlo, no puedo entenderlo.
CAD97
<badguess> replaceusa cadenas pero replaceAllusa expresiones regulares. </badguess>
CalculatorFeline
@CatsAreFluffy bueno, tienes razón ! No sé cómo no me di cuenta de eso: P
CAD97
¿No puedes eliminar la nueva línea?
CalculatorFeline
Bueno, la nueva línea puede eliminarse debido a los semi: s requeridos (que deberían ser .s pero lo que sea)
CalculatorFeline
2

PowerShell, 224 217 235 bytes

param($d,$s)$d=$d-split"`r`n";$p="\s+\$([char[]]$s-join"\")\s";$m=($d|%{($_-split$p)[0].Length}|sort)[-1];$d|%{$l,$c=$_-split$p;$c=if($c){"$s "+(("$c "-split"(.{1,$(87-$m)})\s"|?{$_})-join"`n$(" "*($m+5))$s ")}$l.PadRight($m+5," ")+$c}

Se actualizó la lógica para determinar la longitud máxima de la cadena de código. Actualizada para permitir múltiples separadores que incluyen meta caracteres regex.


Pequeña explicación

Esto toma una cadena completa delimitada de nueva línea para la entrada.

param($d,$s)
# $d is a newline delimited string. $s is the separator.
# Take the string and turn it into a string array. Stored as $d
$d=$d-split"`r`n"
# Save a regex pattern as it is used more than once
$p="\s+\$([char[]]$s-join"\")\s"
# Get the longest string of code's length
$m=($d|%{($_-split$p)[0].Length}|sort)[-1]
# Split each line again into code and comment. Write out each line with formatted explanations based on separator column position $m
$d|%{
# Split the line
$l,$c=$_-split$p
# Build the comment string assuming there is one.
$c=if($c){"$s "+(("$c "-split"(.{1,$(87-$m)})\s"|?{$_})-join"`n$(" "*($m+5))$s ")}
# Pad the right amount of space on the code and add the comment string.
$l.PadRight($m+5," ")+$c
}

Salida de muestra con algo de Lorem Ipsum

shM-crz1dc4."ANDBYOROF     # z = input

     rz1                   # convert input to uppercase
    c   d                  # split input on spaces
         c4."ANDBYOROF     # But I must explain to you how all this mistaken idea of
                           # denouncing pleasure and praising pain was born and I will give
                           # you a complete account of the system, and expound the actual
                           # teachings of the great explorer
   -                       # filter those words out
 hM                        # only take the first letter of all words
s                          # join them into one string
Mate
fuente
@nimi Con suerte, las actualizaciones ahora hacen una mejor solución.
Matt
@nimi ¿Algo más que notaste mal? Aparentemente tengo problemas para leer los últimos días.
Matt
No. Ahora tiene un +1.
nimi
1

MATLAB, 270 265 262 bytes

function d=f(I,s);S=@sprintf;R=@regexprep;m=regexp(I,['\s*\',s]);L=max([m{:}])+4;a=@(x)S('%-*s%s',L,x,s);b=@(x)R(R(x,S('(.{1,%d}(\\s+|$))',93-L),S('$1\n%*s ',L+1,s)),['\n\s*\',s,' $'],'');c=R(I,['(.*?)\s*\',s,'\s*(.*$)'],'${a($1)} ${b($2)}');d=S('%s\n',c{:});end

El programa acepta la entrada Ien forma de una matriz de celdas de cadenas donde cada elemento de la matriz de celdas es una línea separada de la entrada. También acepta una segunda entrada que indica cuál es el carácter del comentario (es decir# ). La función devuelve una cadena de varias líneas que está formateada correctamente.

Breve explicacion

function d = f(I,s)
    %// Setup some shortcuts for commonly-used functions
    S = @sprintf;
    R = @regexprep;

    %// Find the location of the space AFTER each code block but before a comment
    m = regexp(I, ['\s*\',s]);

    %// Compute the maximum column location of the code and add 4 (5 - 1)
    L = max([m{:}]) + 4;

    %// This is a callback for when we detect code
    %// It left justifies and pads the string to L width
    a = @(x)S('%-*s%s', L, x, s);

    %// This is a callback for when we detect a comment.
    b = @(x)R(...
            R(x, ...
                S('(.{1,%d}(\\s|$))', 93 - L), ... Regex for wrapping text to desired width
                S('$1\n%*s ', L+1, s)), ... Append a newline and padding for next line 
            ['\n\s*\',s,' $'], ''); ... Remove the trailing newline (to be improved)

    %// Perform replacement of everything.
    c = R(I, ...
            ['(.*?)\s*\',s,'\s*(.*$)'], ... Match "code comment_char comment"
            '${a($1)} ${b($2)}');   ... Replace using the output of the callbacks

    %// Concatenate all of the strings together with a newline in between
    d=S('%s\n',c{:});
end

Entrada de ejemplo

I = {
    'shM-crz1dc4."ANDBYOROF  # z = input'
    ''
    '     rz1      # convert input to uppercase'
    '    c   d        # split input on spaces'
    '         c4."ANDBYOROF        # create a list of the words from a packed string which shall be ignored'
    '   -          # filter those words out'
    ' hM                # only take the first letter of all words'
    's                   # join them into one string'
};

disp(f(I,'#'));

Salida de ejemplo

shM-crz1dc4."ANDBYOROF     # z = input

     rz1                   # convert input to uppercase
    c   d                  # split input on spaces
         c4."ANDBYOROF     # create a list of the words from a packed string which shall be
                           # ignored
   -                       # filter those words out
 hM                        # only take the first letter of all words
s                          # join them into one string
Suever
fuente