Mio Dio, è pieno di spazi!


42

Alcune persone insistono sull'uso degli spazi per la tabulazione e il rientro.

Per la tabulazione, questo è indiscutibilmente sbagliato. Per definizione, i tabulatori devono essere usati per la tabulazione.

Anche per il rientro, i tabulatori sono oggettivamente superiori:

  • C'è un chiaro consenso nella comunità di Stack Exchange.

  • L'uso di un singolo spazio per il rientro è visivamente spiacevole; l'utilizzo di più di uno è dispendioso.

    Come tutti i programmatori di golf sanno, i programmi dovrebbero essere i più brevi possibili. Non solo consente di risparmiare spazio sul disco rigido, ma anche di ridurre i tempi di compilazione se è necessario elaborare meno byte.

  • Regolando la larghezza della scheda 1 , lo stesso file appare diverso su ciascun computer, quindi tutti possono utilizzare la larghezza del rientro preferita senza modificare il file effettivo.

  • Tutti i buoni editor di testo usano i tabulatori per impostazione predefinita (e definizione).

  • Lo dico e ho sempre ragione!

Purtroppo, non tutti ascoltano la ragione. Qualcuno ti ha inviato un file che sta sbagliando TM e devi correggerlo. Potresti farlo manualmente, ma ce ne saranno altri.

È abbastanza brutto che i distanziatori stiano sprecando il tuo tempo prezioso, quindi decidi di scrivere il programma più breve possibile per occuparti del problema.

Compito

Scrivi un programma o una funzione che procede come segue:

  1. Leggi una singola stringa da STDIN o come argomento da riga di comando o funzione.

  2. Identificare tutte le posizioni in cui sono stati utilizzati spazi per tabulazione o rientro.

    Una serie di spazi è rientro se si verifica all'inizio di una riga.

    Una serie di due o più spazi è tabulazione se non rientro.

    Un singolo spazio che non è rientro può o non può essere stato utilizzato per la tabulazione. Come previsto quando usi lo stesso personaggio per scopi diversi, non esiste un modo semplice per dirlo. Pertanto, diremo che lo spazio è stato utilizzato per la confusione .

  3. Determinare la larghezza della scheda 1 più lunga possibile per la quale tutti gli spazi utilizzati per la tabulazione o il rientro possono essere sostituiti con tabulatori, senza alterare l'aspetto del file.

    Se l'input non contiene né tabulazione né rientro, è impossibile determinare la larghezza della scheda. In questo caso, saltare il passaggio successivo.

  4. Utilizzando la larghezza della linguetta precedentemente determinata, sostituire tutti gli spazi utilizzati per la tabulazione o il rientro con tabulatori.

    Inoltre, quando possibile senza alterare l'aspetto del file, sostituire tutti gli spazi utilizzati per la confusione con i tabulatori. (In caso di dubbio, sbarazzarsi di spazi.)

  5. Restituisce la stringa modificata dalla funzione o stampala su STDOUT.

Esempi

  • Tutti gli spazi di

    a    bc   def  ghij
    

    sono tabulazione.

    Ogni serie di spazi riempie la stringa precedente di caratteri non spaziali con una larghezza di 5, quindi la larghezza della scheda corretta è 5 e l'output corretto 2 è

    a--->bc-->def->ghij
    
  • I primi due spazi di

    ab  cde f
    ghi jk lm
    

    sono tabulazione, le altre confusioni.

    La larghezza scheda corretta è 4, quindi l'uscita corretta 2 è

    ab->cde>f
    ghi>jk lm
    

    L'ultimo spazio rimane intatto, poiché sarebbe reso come due spazi se sostituito da un tabulatore:

    ab->cde>f
    ghi>jk->lm
    
  • Tutti tranne uno spazi di

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

    sono rientri, l'altro è confusione.

    I livelli di rientro sono 0, 4 e 8 spazi, quindi la larghezza della linguetta corretta è 4 e l'uscita 2 corretta è

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

    Lo spazio in ( )sarebbe reso come tre spazi se sostituito da un tabulatore, quindi rimane intatto.

  • I primi due spazi di

      x yz w
    

    sono rientranza, la confusione degli altri.

    La larghezza della linguetta corretta è 2 e l'uscita 2 corretta è

    ->x>yz w
    

    L'ultimo spazio verrebbe reso come due spazi se sostituito da un tabulatore, quindi rimarrà intatto.

  • I primi due spazi di

      xy   zw
    

    sono rientri, gli altri tre sono tabulazione.

    Solo una larghezza di tabulazione di 1 consente di eliminare tutti gli spazi, quindi l'uscita corretta 2 è

    >>xy>>>zw
    
  • Tutti gli spazi di

    a b c d
    

    sono confusione.

    Non esiste una larghezza di scheda più lunga possibile, quindi l'output 2 corretto è

    a b c d
    

Regole aggiuntive

  • L'input consisterà interamente di caratteri ASCII e avanzamenti di riga stampabili.

  • Si può presumere che ci siano al massimo 100 righe di testo e al massimo 100 caratteri per riga.

  • Se si sceglie STDOUT per l'output, è possibile stampare un singolo avanzamento riga finale.

  • Si applicano le regole standard del .


1 La larghezza della scheda è definita come la distanza in caratteri tra due punti di tabulazione consecutivi , utilizzando un carattere a spaziatura singola.
2 Le frecce artistiche ASCII rappresentano i tabulatori Stack Exchange si rifiuta di eseguire il rendering correttamente, per il quale ho inviato una segnalazione di bug. L'output effettivo deve contenere tabulatori effettivi.


9
+1 per mettere finalmente fine a questo problema senza spazio / tabulazione: D
Geobits

2
programs should be as short as possibleCredo di aver trovato il fratello perduto di Arthur Whitney !!
Kirbyfan64sos,


13
Le schede sono demoni malvagi che meritano di essere fatti a pezzi e il loro codice ASCII è caduto in disgrazia fino a quando la loro incompetente mancanza di anima è stata completamente trasformata in una polpa. Voglio dire, +1, bella sfida, anche se puzza di blasfemia. ;)
Maniglia della porta

1
Piangevo ogni volta che un collega aggiungeva una scheda nel mio bellissimo codice rientrato nello spazio. Poi ho scoperto CTRL + K + F in Visual Studio. Lo faccio ogni volta che apro un file modificato. La mia vita ora è migliore.
Michael M.,

Risposte:


5

Pyth, 102 103 byte

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

Provalo online

Un'idea interessante, ma poiché le schede nell'input rompono il concetto, non molto utilizzabile.

Modifica: bug risolto. molte grazie @aditsu


Si blocca su "abc d"
aditsu,

@aditsu merda! Grazie per l'heads-up. Ho bisogno di migliori casi di test: P
Brian Tuck,

5

PowerShell, 414 409 byte

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"

Sono andato avanti e ho usato newline invece di ;dove possibile per rendere più semplice la visualizzazione. Sto usando le terminazioni di linea unix, quindi non dovrebbe influire sul conteggio dei byte.

Come eseguire

Copia il codice in un SpaceMadness.ps1file, quindi reindirizza l'input nello script. Presumo che il file che deve essere convertito sia chiamato taboo.txt:

Da PowerShell:

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

Dal prompt dei comandi:

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

L'ho provato con PowerShell 5, ma dovrebbe funzionare su 3 o versioni successive.

analisi

Ecco un rapido script di PowerShell utile per testare quanto sopra:

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

Metti questo nella stessa directory di SpaceMadness.ps1, io chiamo questo tester.ps1, lo chiamo così:

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

Ti viene l'idea. Sputa il contenuto di ciascun file dopo la conversione, attraversando il [RegEx]::Escape()quale capita di sfuggire sia agli spazi che alle schede, quindi è davvero conveniente vedere cosa è stato effettivamente cambiato.

L'output è simile al seguente (ma con i colori):

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

Spiegazione

La primissima riga definisce la più grande funzione comune di fattore / divisore gnel modo più succinto possibile, che prende un array (numero arbitrario di numeri) e calcola ricorsivamente GCD usando l' algoritmo euclideo .

Lo scopo era quello di capire la "larghezza di scheda più lunga possibile" prendendo l'indice + lunghezza di ogni rientro e tabulazione come definito nella domanda, quindi alimentandolo a questa funzione per ottenere il GCD che penso sia il migliore che possiamo fare per larghezza della scheda. La lunghezza di una confusione sarà sempre 1, quindi non contribuisce a questo calcolo.

$bdefinisce uno scriptblock perché fastidiosamente ho bisogno di chiamare quel pezzo di codice due volte, quindi salvo alcuni byte in quel modo. Questo blocco prende la stringa (o matrice di stringhe) $ne vi esegue una regex ( slso Select-String), restituendo oggetti match. In realtà sto ottenendo sia rientri che tabulazioni in uno qui, il che mi ha davvero salvato un'ulteriore elaborazione catturandoli separatamente.

$nè usato per cose diverse all'interno e all'esterno del ciclo principale (davvero pessimo, ma necessario qui in modo da poterlo incorporare nello $bscriptblock e usarlo sia all'interno che all'esterno del ciclo senza una lunga param()dichiarazione e argomenti passanti.

$sviene assegnata la larghezza della scheda, chiamando il $bblocco sull'array di linee nel file di input, quindi sommando l'indice e la lunghezza di ogni corrispondenza, restituendo l'array delle somme come argomento nella funzione GCD. Quindi $sla dimensione della nostra scheda si ferma ora.

Quindi inizia il loop. Esaminiamo ogni riga nell'array delle righe di input $n. La prima cosa che faccio nel loop è assegnare $n(ambito locale) il valore della riga corrente per il motivo sopra.

$w ottiene il valore della chiamata scriptblock solo per la riga corrente (i rientri e le tabulazioni per la riga corrente).

$cottiene un valore simile, ma troviamo invece tutte le confusioni .

Aggiungo $we $cquali sono gli array, dandomi un array con tutte le corrispondenze di spazio di cui ho bisogno, sortin ordine decrescente per indice, e inizio a scorrere ogni corrispondenza per la riga corrente.

Il tipo è importante. All'inizio ho scoperto che sostituire parti di una stringa in base ai valori dell'indice è una cattiva idea quando la stringa di sostituzione è più piccola e cambia la lunghezza della stringa! Gli altri indici vengono invalidati. Quindi, iniziando con gli indici più alti su ogni riga, mi assicuro di accorciare solo la stringa dalla fine e di spostarmi indietro in modo che gli indici funzionino sempre.

In questo ciclo, $xè nell'indice della corrispondenza corrente ed $lè la lunghezza della corrispondenza corrente. $sin effetti può essere 0e questo causa una divisione fastidiosa per errore zero, quindi sto verificando la sua validità e poi faccio i conti.

Il !(($x+$l)%$s)bit è l'unico punto in cui posso controllare per vedere se un confusione deve essere sostituita con una linguetta o meno. Se l'indice più la lunghezza divisa per la larghezza della scheda non ha resto, allora è bene andare a sostituire questa corrispondenza con una scheda (quella matematica funzionerà sempre su rientri e tabulazioni , perché la loro dimensione è ciò che ha determinato la larghezza della scheda iniziare con).

Per la sostituzione, ogni iterazione del ciclo di corrispondenza funziona sulla riga corrente dell'input, quindi è un insieme cumulativo di sostituzioni. La regex cerca solo $lspazi preceduti da $xqualsiasi personaggio. Lo sostituiamo con $l/$scaratteri di tabulazione (o 1 se quel numero è inferiore a zero).

Questa parte (($l/$s),1-ge1)[0]è un modo stravagante di dire if (($l/$s) -lt 0) { 1 } else { $l/$s }o in alternativa [Math]::Max(1,($l/$s)). Crea un array di $l/$se 1, quindi utilizza -ge 1per restituire un array contenente solo gli elementi maggiori o uguali a uno, quindi accetta il primo elemento. Viene fornito in pochi byte più corto della [Math]::Maxversione.

Quindi, una volta terminati tutti i rimpiazzi, la riga corrente viene restituita dall'iterazione ForEach-Object( %) e quando vengono restituiti tutti (una matrice di linee fisse), viene modificata -joincon le nuove linee (poiché all'inizio abbiamo diviso le nuove linee).

Sento che c'è spazio per miglioramenti qui che sono troppo sfinito per catturare in questo momento, ma forse vedrò qualcosa più tardi.

Schede 4 lyfe


4

PHP - 278 210 byte

La funzione funziona testando ciascuna larghezza della scheda, iniziando con un valore di 100, la lunghezza massima di una linea e quindi la larghezza massima della scheda.

Per ogni larghezza di tabulazione, dividiamo ogni linea in "blocchi" di quella lunghezza. Per ciascuno di questi blocchi:

  • Se concatenando l'ultimo carattere del blocco precedente con questo blocco, troviamo due spazi consecutivi prima di un personaggio, abbiamo una rientranza o una tabulazione che non può essere trasformata in spazio senza alterarne l'aspetto; proviamo la larghezza della scheda successiva.
  • Altrimenti, se l'ultimo carattere è uno spazio, eliminiamo gli spazi alla fine del blocco, aggiungiamo un tabulatore e memorizziamo il tutto.
  • Altrimenti, memorizziamo semplicemente il blocco.

Dopo aver analizzato ogni blocco di una linea, memorizziamo un avanzamento riga. Se tutti i blocchi di tutte le linee sono stati analizzati con successo, restituiamo la stringa che abbiamo memorizzato. Altrimenti, se ogni larghezza di tabulazione strettamente positiva è stata provata, non vi era né tabulazione, né rientro, e restituiamo la stringa originale.

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

Ecco la versione ungolfed:

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 ringraziamento speciale a DankMemes per aver salvato 2 byte.


1
Puoi salvare 2 byte usando for($t=101;--$t;)invece difor($t=100;$t;--$t)
DankMemes il

4

CJam, 112

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

Provalo online

Ho dovuto rispondere a questa sfida, perché devo fare la mia parte per aiutare a liberare il mondo da questo abominio. Le schede sono ovviamente superiori, ma purtroppo alcune persone non possono essere ragionate.

Spiegazione:

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

1

PowerShell , 165 160 153 152 142 138 137 byte

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

Provalo online!

Meno golf:

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
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.