Meu Deus, está cheio de espaços!

42

Algumas pessoas insistem em usar espaços para tabulação e recuo.

Para tabulação, isso é indiscutivelmente errado. Por definição, os tabuladores devem ser usados ​​para tabulação.

Mesmo para indentação, os tabuladores são objetivamente superiores:

  • Existe um consenso claro na comunidade Stack Exchange.

  • Usar um único espaço para indentação é visualmente desagradável; usar mais de um é um desperdício.

    Como todos os golfistas sabem, os programas devem ser o mais curtos possível. Além de economizar espaço no disco rígido, os tempos de compilação também são reduzidos se for necessário processar menos bytes.

  • Ao ajustar a largura da guia 1 , o mesmo arquivo parece diferente em cada computador, para que todos possam usar sua largura de recuo favorita sem modificar o arquivo real.

  • Todos os bons editores de texto usam tabuladores por padrão (e definição).

  • Eu digo e estou sempre certa!

Infelizmente, nem todo mundo ouve a razão. Alguém enviou a você um arquivo que está fazendo algo errado na TM e você precisa corrigi-lo. Você poderia fazê-lo manualmente, mas haverá outros.

Já é ruim o suficiente que os espaçadores estejam desperdiçando seu precioso tempo, então você decide escrever o programa mais curto possível para resolver o problema.

Tarefa

Escreva um programa ou uma função que faça o seguinte:

  1. Leia uma única sequência de caracteres de STDIN ou como um argumento de linha de comando ou função.

  2. Identifique todos os locais em que os espaços foram usados ​​para tabulação ou recuo.

    Uma sequência de espaços é um recuo se ocorrer no início de uma linha.

    Uma execução de dois ou mais espaços é tabulada se não for recuo.

    Um único espaço que não é recuo pode ou não ter sido usado para tabulação. Como esperado, quando você usa o mesmo personagem para propósitos diferentes, não há uma maneira fácil de dizer. Portanto, diremos que o espaço foi usado para confusão .

  3. Determine a maior largura possível de tabulação 1, para a qual todos os espaços usados ​​para tabulação ou recuo podem ser substituídos por tabuladores, sem alterar a aparência do arquivo.

    Se a entrada não contiver tabulação nem indentação, é impossível determinar a largura da tabulação. Nesse caso, pule a próxima etapa.

  4. Usando a largura da guia previamente determinada, substitua todos os espaços usados ​​para tabulação ou recuo pelos tabuladores.

    Além disso, sempre que possível, sem alterar a aparência do arquivo, substitua todos os espaços usados ​​para confusão pelos tabuladores. (Em caso de dúvida, livre-se dos espaços.)

  5. Retorne a string modificada da sua função ou imprima-a em STDOUT.

Exemplos

  • Todos os espaços de

    a    bc   def  ghij
    

    são tabulações.

    Cada execução de espaços preenche a sequência anterior de caracteres não espaciais para uma largura de 5, portanto, a largura correta da guia é 5 e a saída correta 2 é

    a--->bc-->def->ghij
    
  • Os dois primeiros espaços de

    ab  cde f
    ghi jk lm
    

    são tabulações, os outros confusão.

    A largura correta da guia é 4, portanto a saída correta 2 é

    ab->cde>f
    ghi>jk lm
    

    O último espaço permanece intocado, pois seria renderizado como dois espaços se substituído por um tabulador:

    ab->cde>f
    ghi>jk->lm
    
  • Todos os espaços, exceto um

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

    são recuos, o outro é confusão.

    Os níveis de indentação são 0, 4 e 8 espaços, portanto a largura correta da guia é 4 e a saída correta 2 é

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

    O espaço em ( )seria renderizado como três espaços se substituído por um tabulador, permanecendo intocado.

  • Os dois primeiros espaços de

      x yz w
    

    são recuos, os outros confundem.

    A largura da guia adequada é 2 e a saída correta 2 é

    ->x>yz w
    

    O último espaço seria renderizado como dois espaços se substituído por um tabulador, permanecendo intocado.

  • Os dois primeiros espaços de

      xy   zw
    

    são recuos, os outros três são tabulação.

    Somente uma largura de guia de 1 permite eliminar todos os espaços; portanto, a saída correta 2 é

    >>xy>>>zw
    
  • Todos os espaços de

    a b c d
    

    são confusão.

    Não existe a maior largura possível da guia, portanto a saída correta 2 é

    a b c d
    

Regras adicionais

  • A entrada consistirá inteiramente em caracteres ASCII imprimíveis e alimentações de linha.

  • Você pode supor que haja no máximo 100 linhas de texto e no máximo 100 caracteres por linha.

  • Se você escolher STDOUT para saída, poderá imprimir um único avanço de linha à direita.

  • Aplicam-se as regras padrão de .


1 A largura da guia é definida como a distância em caracteres entre duas paradas consecutivas , usando uma fonte monoespaçada.
2 As setas de arte ASCII representam os tabuladores que o Stack Exchange se recusa a renderizar corretamente, para o qual enviei um relatório de erro. A saída real deve conter tabuladores reais.

Dennis
fonte
9
+1 por finalmente colocar esse problema absurdo de espaço / guia para descansar: D
Geobits
2
programs should be as short as possibleAcredito ter encontrado o irmão há muito perdido de Arthur Whitney !!
Kirbyfan64sos # 03/09/2015
6
@ Dennis " Dito isto, apenas um idiota usaria guias para formatar seu código. " Consenso claro, não é?
primo
13
Guias são demonspawn profanas que merecem ter seus bits separados e seu código ASCII desonrado até que sua incompetente falta de alma tenha sido completamente transformada em polpa. Errr, quero dizer, +1, bom desafio, mesmo que cheire a blasfêmia. ;)
Maçaneta da porta
1
Eu chorava cada vez que um colega adicionava uma guia no meu belo código recuado de espaço. Então eu descobri CTRL + K + F no Visual Studio. Faço isso sempre que abro um arquivo modificado. Minha vida está melhor agora.
Michael M.

Respostas:

5

Pitão, 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

Experimente Online

Idéia interessante, mas como as guias na entrada quebram o conceito, não é muito utilizável.

Edit: Fixed bug. muito obrigado @aditsu

Brian Tuck
fonte
Falha no "abc d"
aditsu 08/09/15
@aditsu crap! Thanx pelo aviso. Preciso de melhores casos de teste: 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"

Fui em frente e usei novas linhas em vez de, sempre ;que possível, para facilitar a exibição. Estou usando finais de linha unix, para que não afete a contagem de bytes.

Como executar

Copie o código no SpaceMadness.ps1arquivo e envie a entrada para o script. Assumirei que o arquivo que precisa ser convertido é chamado taboo.txt:

No PowerShell:

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

No prompt de comando:

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

Eu testei com o PowerShell 5, mas deve funcionar em 3 ou superior.

Teste

Aqui está um script rápido do PowerShell que é útil para testar o acima:

[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"
    }
}

Coloque isso no mesmo diretório que SpaceMadness.ps1, eu chamo este tester.ps1, assim:

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

Você entendeu a ideia. Ele cospe o conteúdo de cada arquivo após a conversão, através do [RegEx]::Escape()qual acontece escapar dos espaços e das guias, por isso é realmente conveniente ver o que realmente foi alterado.

A saída é assim (mas com cores):

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

Explicação

A primeira linha define a maior função comum de fator / divisor da maneira gmais sucinta possível, que pega uma matriz (número arbitrário de números) e calcula o GCD recursivamente usando o algoritmo euclidiano .

O objetivo disso era descobrir a "maior largura possível de tabulação", obtendo o índice + o comprimento de cada recuo e tabulação conforme definido na pergunta e alimentando-o com essa função para obter o GCD que eu acho que é o melhor que podemos faça para a largura da guia. O comprimento de uma confusão sempre será 1, portanto, nada contribui para esse cálculo.

$bdefine um scriptblock porque irritantemente eu preciso chamar esse trecho de código duas vezes, então eu salvo alguns bytes dessa maneira. Este bloco pega a string (ou matriz de strings) $ne executa uma regex nela ( slsou Select-String), retornando objetos correspondentes. Na verdade, estou recebendo recuos e tabulações em um aqui, o que realmente me salvou de processamento extra ao capturá-los separadamente.

$né usado para coisas diferentes dentro e fora do loop principal (muito ruim, mas necessário aqui para que eu possa incorporá-lo no $bscriptblock e usá-lo dentro e fora do loop sem uma param()declaração longa e sem argumentos).

$srecebe a largura da guia, chamando o $bbloco na matriz de linhas no arquivo de entrada, somando o índice e o comprimento de cada correspondência, retornando a matriz das somas como argumento para a função GCD. Então $s, o tamanho da nossa guia é interrompido agora.

Então o loop começa. Nós iteramos sobre cada linha na matriz de linhas de entrada $n. A primeira coisa que faço no loop é atribuir $n(escopo local) o valor da linha atual pelo motivo acima.

$w obtém o valor da chamada de scriptblock apenas para a linha atual (os recuos e tabulações da linha atual).

$crecebe um valor semelhante, mas, em vez disso, encontramos todas as confusões .

Eu adiciono $we $cquais são matrizes, fornecendo uma matriz com todas as correspondências de espaço necessárias, sortem ordem decrescente por índice, e começo a iterar sobre cada correspondência da linha atual.

O tipo é importante. Desde o início, descobri da maneira mais difícil que substituir partes de uma string com base nos valores de índice é uma má idéia quando a string de substituição é menor e altera o comprimento da string! Os outros índices são invalidados. Portanto, iniciando com os índices mais altos de cada linha, certifico-me de que apenas reduza a string do final e mova para trás para que os índices funcionem sempre.

Nesse loop, $xestá no índice da correspondência atual e $lé o comprimento da correspondência atual. $sde fato, pode ser 0e isso causa uma divisão traquina por erro zero, por isso estou verificando sua validade e depois fazendo as contas.

A questão !(($x+$l)%$s)é o único ponto em que eu verifico se uma confusão deve ser substituída por uma guia ou não. Se o índice mais o comprimento dividido pela largura da guia não tiver resto, é bom substituir esta correspondência por uma guia (essa matemática sempre funcionará nas indentações e tabulações , porque seu tamanho é o que determinou a largura da guia começar com).

Para a substituição, cada iteração do loop de correspondência funciona na linha atual da entrada, portanto, é um conjunto cumulativo de substituições. A regex procura apenas $lespaços precedidos por $xqualquer caractere. Nós o substituímos por $l/$scaracteres de tabulação (ou 1 se esse número estiver abaixo de zero).

Esta parte (($l/$s),1-ge1)[0]é uma maneira complicada de dizer if (($l/$s) -lt 0) { 1 } else { $l/$s }ou alternativamente [Math]::Max(1,($l/$s)). Ele cria uma matriz de $l/$se 1, em seguida, usa -ge 1para retornar uma matriz que contém apenas os elementos que são maiores ou iguais a um e, em seguida, pega o primeiro elemento. Ele vem em alguns bytes menor que a [Math]::Maxversão.

Portanto, quando todas as substituições forem concluídas, a linha atual será retornada da iteração ForEach-Object( %) e, quando todas elas forem retornadas (uma matriz de linhas fixas), serão adicionadas -joinnovas linhas (já que dividimos as novas linhas no início).

Eu sinto que há espaço para melhorias aqui que estou muito cansada para pegar agora, mas talvez eu veja algo mais tarde.

Guias 4 lyfe

briantist
fonte
4

PHP - 278 210 bytes

A função funciona testando cada largura da guia, começando com um valor de 100, o comprimento máximo de uma linha e, portanto, a largura máxima da guia.

Para cada largura da guia, dividimos cada linha em "blocos" desse comprimento. Para cada um desses blocos:

  • Se, concatenando o último caractere do bloco anterior com esse bloco, encontramos dois espaços consecutivos antes de um caractere, temos um recuo ou uma tabulação que não pode ser transformada em espaço sem alterar a aparência; tentamos a próxima largura da guia.
  • Caso contrário, se o último caractere for um espaço, separamos os espaços no final do bloco, adicionamos um tabulador e memorizamos tudo.
  • Caso contrário, apenas memorizamos o bloco.

Após a análise de cada bloco de uma linha, memorizamos um avanço de linha. Se todos os blocos de todas as linhas foram analisados ​​com sucesso, retornamos a string que memorizamos. Caso contrário, se cada largura de tabulação estritamente positiva tiver sido tentada, não haverá tabulação nem recuo, e retornaremos a string 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;}

Aqui está a versão não destruída:

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

Agradecimentos especiais ao DankMemes por salvar 2 bytes.

Blackhole
fonte
1
Você pode salvar 2 bytes usando em for($t=101;--$t;)vez 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*

Experimente online

Eu tive que responder a esse desafio, porque devo fazer minha parte para ajudar a livrar o mundo dessa abominação. As guias são obviamente superiores, mas, infelizmente, algumas pessoas simplesmente não podem ser justificadas.

Explicação:

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
fonte
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]

Experimente online!

Menos golfe:

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
confuso
fonte