¡Dios mío, está lleno de espacios!

42

Algunas personas insisten en usar espacios para tabulación y sangría.

Para la tabulación, eso es indiscutiblemente incorrecto. Por definición, los tabuladores deben usarse para la tabulación.

Incluso para la sangría, los tabuladores son objetivamente superiores:

  • Existe un claro consenso en la comunidad de Stack Exchange.

  • Usar un solo espacio para la sangría es visualmente desagradable; usar más de uno es un desperdicio.

    Como todos los jugadores de bacalao y golf saben, los programas deben ser lo más cortos posible. No solo ahorra espacio en el disco duro, sino que también se reducen los tiempos de compilación si hay que procesar menos bytes.

  • Al ajustar el ancho de la pestaña 1 , el mismo archivo se ve diferente en cada computadora, por lo que todos pueden usar su ancho de sangría favorito sin modificar el archivo real.

  • Todos los buenos editores de texto usan tabuladores por defecto (y definición).

  • ¡Lo digo y siempre tengo razón!

Lamentablemente, no todos escuchan la razón. Alguien le ha enviado un archivo que lo está haciendo mal TM y usted tiene que arreglarlo. Podrías hacerlo manualmente, pero habrá otros.

Ya es bastante malo que los espaciadores estén desperdiciando su valioso tiempo, por lo que decide escribir el programa más corto posible para solucionar el problema.

Tarea

Escriba un programa o una función que haga lo siguiente:

  1. Lea una sola cadena desde STDIN o como una línea de comando o argumento de función.

  2. Identifique todas las ubicaciones donde se hayan utilizado espacios para tabulación o sangría.

    Una serie de espacios es una sangría si ocurre al comienzo de una línea.

    Una serie de dos o más espacios es tabulación si no es una sangría.

    Un espacio único que no es sangrado puede o no haber sido usado para tabulación. Como era de esperar cuando usas el mismo personaje para diferentes propósitos, no hay una manera fácil de saberlo. Por lo tanto, diremos que el espacio se ha utilizado para la confusión .

  3. Determine el ancho de pestaña 1 más largo posible para el que todos los espacios utilizados para tabulación o sangría se puedan reemplazar con tabuladores, sin alterar la apariencia del archivo.

    Si la entrada no contiene ni tabulación ni sangría, es imposible determinar el ancho de la pestaña. En este caso, omita el siguiente paso.

  4. Usando el ancho de tabulación previamente determinado, reemplace todos los espacios utilizados para tabulación o sangría con tabuladores.

    Además, siempre que sea posible sin alterar la apariencia del archivo, reemplace todos los espacios utilizados para la confusión con tabuladores. (En caso de duda, elimine los espacios).

  5. Devuelva la cadena modificada de su función o imprímala en STDOUT.

Ejemplos

  • Todos los espacios de

    a    bc   def  ghij
    

    son tabulación

    Cada serie de espacios rellena la cadena anterior de caracteres que no son espacios con un ancho de 5, por lo que el ancho de tabulación correcto es 5 y el resultado correcto 2 es

    a--->bc-->def->ghij
    
  • Los dos primeros espacios de

    ab  cde f
    ghi jk lm
    

    son tabulación, los otros confunden.

    El ancho de pestaña correcto es 4, por lo que la salida correcta 2 es

    ab->cde>f
    ghi>jk lm
    

    El último espacio permanece intacto, ya que se representaría como dos espacios si se reemplaza por un tabulador:

    ab->cde>f
    ghi>jk->lm
    
  • Todos menos uno espacios de

    int
        main( )
        {
            puts("TABS!");
        }
    

    son sangría, el otro es confusión.

    Los niveles de sangría son 0, 4 y 8 espacios, por lo que el ancho de pestaña correcto es 4 y la salida correcta 2 es

    int
    --->main( )
    --->{
    --->--->puts("TABS!");
    --->}
    

    El espacio en ( )se representaría como tres espacios si se reemplaza por un tabulador, por lo que permanece intacto.

  • Los dos primeros espacios de

      x yz w
    

    son sangría, los demás confusión.

    El ancho de pestaña adecuado es 2 y la salida correcta 2 es

    ->x>yz w
    

    El último espacio se representaría como dos espacios si se reemplaza por un tabulador, por lo que permanece intacto.

  • Los dos primeros espacios de

      xy   zw
    

    son sangría, los otros tres son tabulación.

    Solo un ancho de tabulación de 1 permite eliminar todos los espacios, por lo que la salida correcta 2 es

    >>xy>>>zw
    
  • Todos los espacios de

    a b c d
    

    son confusión

    No existe el ancho de pestaña más largo posible, por lo que la salida correcta 2 es

    a b c d
    

Reglas adicionales

  • La entrada consistirá completamente en caracteres ASCII imprimibles y saltos de línea.

  • Puede suponer que hay como máximo 100 líneas de texto y como máximo 100 caracteres por línea.

  • Si elige STDOUT para la salida, puede imprimir un solo salto de línea final.

  • Aplican reglas estándar de .


1 El ancho de tabulación se define como la distancia en caracteres entre dos tabulaciones consecutivas , utilizando una fuente monoespaciada.
2 Las flechas de arte ASCII representan los tabuladores que Stack Exchange se niega a representar correctamente, para lo cual he enviado un informe de error. La salida real debe contener tabuladores reales.

Dennis
fuente
99
+1 para finalmente poner fin a esta cuestión absurda de espacio / tabulación: D
Geobits
2
programs should be as short as possible¡Creo que he encontrado al hermano perdido de Arthur Whitney!
kirbyfan64sos
13
Las pestañas son engendros demoníacos impíos que merecen que se rompan sus pedazos y que su código ASCII se deshonre hasta que su falta de alma incompetente se haya convertido en una pulpa. Errr, quiero decir, +1, buen desafío, a pesar de que huele a blasfemia. ;)
Pomo de la puerta
1
Estaba llorando cada vez que un colega agregaba una pestaña en mi hermoso espacio con código sangrado. Luego descubrí CTRL + K + F en Visual Studio. Lo hago cada vez que abro un archivo modificado. Mi vida es mejor ahora.
Michael M.

Respuestas:

5

Pyth, 102 103 bytes

=T|u?<1hHiGeHGsKmtu++J+hHhGlhtH+tG]+HJ.b,YN-dk<1u+G?H1+1.)Gd]0]0cR\ .zZ8VKVNp?%eNT*hNd*/+tThNTC9p@N1)pb

Pruébalo en línea

Idea interesante, pero dado que las pestañas en la entrada rompen el concepto, no es muy útil.

Editar: Corregido error. muchas gracias @aditsu

Brian Tuck
fuente
Se bloquea en "abc d"
aditsu
@aditsu mierda! Gracias por el heads-up. Necesito mejores casos de prueba: P
Brian Tuck
5

PowerShell, 414 409 bytes

function g($a){if($a.length-gt2){g $a[0],(g $a[1..100])}else{if(!$a[1]){$a[0]}else{g $a[1],($a[0]%$a[1])}}}{$a[0]}else{g $a[1],($a[0]%$a[1])}}}
$b={($n|sls '^ +|(?<!^)  +' -a).Matches}
$n=$input-split"`n"
$s=g(&$b|%{$_.Index+$_.Length})
($n|%{$n=$_
$w=@(&$b)
$c=($n|sls '(?<!^| ) (?! )'-a).Matches
$w+$c|sort index -d|%{$x=$_.Index
$l=$_.Length
if($s-and!(($x+$l)%$s)){$n=$n-replace"(?<=^.{$x}) {$l}",("`t"*(($l/$s),1-ge1)[0])}}
$n})-join"`n"

Seguí adelante y usé nuevas líneas en lugar de ;donde sea posible para facilitar la visualización. Estoy usando terminaciones de línea de Unix, por lo que no debería afectar el recuento de bytes.

Cómo ejecutar

Copie el código en el SpaceMadness.ps1archivo, luego canalice la entrada en el script. Asumiré que el archivo que necesita conversión se llama taboo.txt:

De PowerShell:

cat .\taboo.txt | .\SpaceMadness.ps1

Desde el símbolo del sistema:

type .\taboo.txt | powershell.exe -File .\SpaceMadness.txt

Lo probé con PowerShell 5, pero debería funcionar en 3 o superior.

Pruebas

Aquí hay un script rápido de PowerShell que es útil para probar lo anterior:

[CmdletBinding()]
param(
    [Parameter(
        Mandatory=$true,
        ValueFromPipeline=$true
    )]
    [System.IO.FileInfo[]]
    $File
)

Begin {
    $spaces = Join-Path $PSScriptRoot SpaceMadness.ps1
}

Process {
     $File | ForEach-Object {
        $ex = Join-Path $PSScriptRoot $_.Name 
        Write-Host $ex -ForegroundColor Green
        Write-Host ('='*40) -ForegroundColor Green
        (gc $ex -Raw | & $spaces)-split'\r?\n'|%{[regex]::Escape($_)} | Write-Host -ForegroundColor White -BackgroundColor Black
        Write-Host "`n"
    }
}

Ponga esto en el mismo directorio que SpaceMadness.ps1, yo llamo a este tester.ps1, llámelo así:

"C:\Source\SomeFileWithSpaces.cpp" | .\tester.ps1
.\tester.ps1 C:\file1.txt,C:\file2.txt
dir C:\Source\*.rb -Recurse | .\tester.ps1

Tienes la idea. Escupe el contenido de cada archivo después de la conversión, y se ejecuta [RegEx]::Escape()para escapar de los espacios y las pestañas, por lo que es realmente conveniente ver lo que realmente se ha cambiado.

La salida se ve así (pero con colores):

C:\Scripts\Powershell\Golf\ex3.txt
========================================
int
\tmain\(\ \)
\t\{
\t\tputs\("TABS!"\);
\t}

Explicación

La primera línea define la mayor función común de factor / divisor gtan sucintamente como pude manejar, que toma una matriz (número arbitrario de números) y calcula GCD de forma recursiva utilizando el algoritmo euclidiano .

El propósito de esto era determinar el "ancho de tabulación más largo posible" tomando el índice + longitud de cada sangría y tabulación como se define en la pregunta, luego introduciéndolo en esta función para obtener el MCD que creo que es lo mejor que podemos hacer para el ancho de la pestaña. La longitud de una confusión siempre será 1, por lo que no contribuye en nada a este cálculo.

$bdefine un bloque de script porque molestamente necesito llamar a ese código dos veces, así que guardo algunos bytes de esa manera. Este bloque toma la cadena (o matriz de cadenas) $ny ejecuta una expresión regular en ella ( slso Select-String), devolviendo objetos coincidentes. De hecho, estoy obteniendo sangrías y tabulaciones en una aquí, lo que realmente me ahorró un procesamiento adicional al capturarlas por separado.

$nse usa para diferentes cosas dentro y fuera del bucle principal (realmente malo, pero necesario aquí para que pueda incrustarlo en $bel bloque de script y usarlo tanto dentro como fuera del bucle sin una larga param()declaración y sin pasar argumentos.

$sse le asigna el ancho de la pestaña, llamando al $bbloque en la matriz de líneas en el archivo de entrada, luego sumando el índice y la longitud de cada coincidencia, devolviendo la matriz de las sumas como un argumento en la función GCD. Entonces, $sel tamaño de nuestra pestaña se detiene ahora.

Entonces comienza el ciclo. Repetimos cada línea en la matriz de líneas de entrada $n. Lo primero que hago en el bucle es asignar $n(alcance local) el valor de la línea actual por la razón anterior.

$w obtiene el valor de la llamada al scriptblock solo para la línea actual (las sangrías y tabulaciones para la línea actual).

$cobtiene un valor similar, pero en cambio encontramos todas las confusiones .

Sumo $wy $ccuáles son matrices, dándome una matriz con todas las coincidencias de espacio que necesito, sorten orden descendente por índice, y empiezo a iterar sobre cada coincidencia para la línea actual.

El tipo es importante. Al principio descubrí de la manera más difícil que reemplazar partes de una cadena basada en valores de índice es una mala idea cuando la cadena de reemplazo es más pequeña y cambia la longitud de la cadena. Los otros índices quedan invalidados. Entonces, al comenzar con los índices más altos en cada línea, me aseguro de que solo acorte la cadena desde el final y me muevo hacia atrás para que los índices siempre funcionen.

En este bucle, $xestá en el índice de la coincidencia actual y $les la duración de la coincidencia actual. $sde hecho puede ser 0y eso causa una molesta división por cero error, así que estoy verificando su validez y luego haciendo los cálculos.

El !(($x+$l)%$s)bit es el único punto en el que verifico si una confusión debe reemplazarse con una pestaña o no. Si el índice más la longitud dividida por el ancho de la pestaña no tiene resto, entonces es bueno reemplazar esta coincidencia con una pestaña (esa matemática siempre funcionará en las muescas y tabulaciones , porque su tamaño es lo que determina el ancho de la pestaña para empezar).

Para el reemplazo, cada iteración del bucle de coincidencia funciona en la línea actual de la entrada, por lo que es un conjunto acumulativo de reemplazos. La expresión regular solo busca $lespacios precedidos por $xcualquier carácter. Lo reemplazamos con $l/$scaracteres de tabulación (o 1 si ese número está por debajo de cero).

Esta parte (($l/$s),1-ge1)[0]es una forma elegante y complicada de decir if (($l/$s) -lt 0) { 1 } else { $l/$s }o alternativamente [Math]::Max(1,($l/$s)). Forma una matriz de $l/$sy 1, luego usa -ge 1para devolver una matriz que contiene solo los elementos que son mayores o iguales a uno, luego toma el primer elemento. Viene en unos pocos bytes más cortos que la [Math]::Maxversión.

Entonces, una vez que se realizan todos los reemplazos, la línea actual se devuelve desde la iteración ForEach-Object( %), y cuando se devuelven todos (una matriz de líneas fijas), se -joinedita con nuevas líneas (ya que al principio nos dividimos en nuevas líneas).

Siento que hay margen de mejora aquí que estoy demasiado agotado para atrapar en este momento, pero tal vez veré algo más tarde.

Tabs 4 lyfe

briantista
fuente
4

PHP - 278 210 bytes

La función funciona probando el ancho de cada pestaña, comenzando con un valor de 100, la longitud máxima de una línea y, por lo tanto, el ancho máximo de la pestaña.

Para cada ancho de pestaña, dividimos cada línea en "bloques" de esa longitud. Para cada uno de estos bloques:

  • Si, al concatenar el último carácter del bloque anterior con este bloque, encontramos dos espacios consecutivos antes de un carácter, tenemos una sangría o una tabulación que no se puede transformar en espacio sin alterar la apariencia; intentamos el siguiente ancho de pestaña.
  • De lo contrario, si el último carácter es un espacio, eliminamos espacios al final del bloque, agregamos un tabulador y memorizamos todo.
  • De lo contrario, solo memorizamos el bloque.

Una vez que cada bloque de una línea ha sido analizado, memorizamos un salto de línea. Si todos los bloques de todas las líneas se analizaron con éxito, devolvemos la cadena que hemos memorizado. De lo contrario, si se ha intentado cada ancho de pestaña estrictamente positivo, no hubo tabulación ni sangría, y devolvemos la cadena original.

function($s){for($t=101;--$t;){$c='';foreach(split('
',$s)as$l){$e='';foreach(str_split($l,$t)as$b){if(ereg('  [^ ]',$e.$b))continue 3;$c.=($e=substr($b,-1))==' '?rtrim($b).'   ':$b;}$c.='
';}return$c;}return$s;}

Aquí está la versión sin golf:

function convertSpacesToTabs($string)
{
    for ($tabWidth = 100; $tabWidth > 0; --$tabWidth)
    {
        $convertedString = '';
        foreach (explode("\n", $string) as $line)
        {
            $lastCharacter = '';
            foreach (str_split($line, $tabWidth) as $block)
            {
                if (preg_match('#  [^ ]#', $lastCharacter.$block))
                {
                    continue 3;
                }

                $lastCharacter = substr($block, -1);
                if ($lastCharacter == ' ')
                {
                    $convertedString .= rtrim($block) ."\t";
                }
                else
                {
                    $convertedString .= $block;
                }
            }

            $convertedString .= "\n";
        }

        return $convertedString;
    }

    return $string;
}

Un agradecimiento especial a DankMemes por guardar 2 bytes.

Agujero negro
fuente
1
Puede guardar 2 bytes utilizando en for($t=101;--$t;)lugar defor($t=100;$t;--$t)
DankMemes
4

CJam, 112

qN/_' ff=:e`{0:X;{_0=X+:X+}%}%_:+{~;\(*},2f=0\+{{_@\%}h;}*:T;\.f{\~\{@;1$({;(T/)9c*}{\;T{T%}&S9c?}?}{1$-@><}?}N*

Pruébalo en línea

Tenía que responder a este desafío, porque debo hacer mi parte para ayudar a librar al mundo de esta abominación. Las pestañas son obviamente superiores, pero lamentablemente, algunas personas simplemente no pueden ser razonadas.

Explicación:

qN/          read input and split into lines
_            duplicate the array (saving one copy for later)
' ff=        replace each character in each line with 0/1 for non-space/space
:e`          RLE-encode each line (obtaining chunks of spaces/non-spaces)
{…}%         transform each line
  0:X;       set X=0
  {…}%       transform each chunk, which is a [length, 0/1] array
    _0=      copy the first element (the length)
    X+:X     increment X by it
    +        and append to the array; this is the end position for the chunk
_            duplicate the array (saving one copy for later)
:+           join the lines (putting all the chunks together in one array)
{…},         filter the array using the block to test each chunk
  ~          dump the chunk (length, 0/1, end) on the stack
  ;          discard the end position
  \(         bring the length to the top and decrement it
  *          multiply the 2 values (0/1 for non-space/space, and length-1)
              the result is non-zero (true) iff it's a chunk of at least 2 spaces
2f=          get all the end positions of the multiple-space chunks
0\+          prepend a 0 to deal with the empty array case
{…}*         fold the array using the block
  {_@\%}h;   calculate gcd of 2 numbers
:T;          save the resulting value (gcd of all numbers) in variable T
\            swap the 2 arrays we saved earlier (input lines and chunks)
.f{…}        for each chunk and its corresponding line
  \~         bring the chunk to the top and dump it on the stack
              (length, 0/1, end position)
  \          swap the end position with the 0/1 space indicator
  {…}        if 1 (space)
    @;       discard the line text
    1$(      copy the chunk length and decrement it
    {…}      if non-zero (multiple spaces)
      ;      discard the end position
      (T/)   divide the length by T, rounding up
      9c*    repeat a tab character that many times
    {…}      else (single space)
      \;     discard the length
      T{…}&  if T != 0
        T%   calculate the end position mod T
      S9c?   if non-zero, use a space, else use a tab
    ?        end if
  {…}        else (non-space)
    1$-      copy the length and subtract it from the end position
              to get the start position of the chunk
    @>       slice the line text beginning at the start position
    <        slice the result ending at the chunk length
              (this is the original chunk text)
  ?          end if
N*           join the processed lines using a newline separator
aditsu
fuente
1

PowerShell , 165 160 153 152 142 138 137 bytes

param($s)@((0..99|%{$s-split"(
|..{0,$_})"-ne''-replace(' '*!$_*($s[0]-ne32)+' +$'),"`t"-join''})-notmatch'(?m)^ |\t '|sort{$_|% Le*})[0]

Pruébalo en línea!

Menos golfizado:

param($spacedString)

$tabed = 0..99|%{
    $spacedString `
        -split "(\n|..{0,$_})" -ne '' `
        -replace (' '*!$_*($spacedString[0]-ne32)+' +$'),"`t" `
        -join ''
}

$validated = $tabed -notmatch '(?m)^ |\t '

$sorted = $validated|sort{$_|% Length}    # sort by a Length property

@($sorted)[0]  # $shortestProgram is an element with minimal length
mazzy
fuente