Mein Gott, es ist voller Räume!

42

Einige Leute bestehen darauf, Leerzeichen für Tabellierung und Einrückung zu verwenden.

Für die Tabellierung ist das unbestreitbar falsch. Per Definition müssen Tabulatoren für die Tabellierung verwendet werden.

Auch beim Einrücken sind Tabulatoren objektiv überlegen:

  • In der Stack Exchange-Community besteht ein klarer Konsens .

  • Die Verwendung eines einzelnen Einrückungsbereichs ist visuell unangenehm. mehr als eine zu verwenden ist verschwenderisch.

    Da alle Dorsch e Golf ers wissen, Programme sollten so kurz wie möglich sein. Dies spart nicht nur Festplattenspeicher, sondern auch Kompilierungszeiten, wenn weniger Bytes verarbeitet werden müssen.

  • Durch Anpassen der Registerbreite 1 sieht dieselbe Datei auf jedem Computer anders aus, sodass jeder seine bevorzugte Einzugsbreite verwenden kann, ohne die eigentliche Datei zu ändern.

  • Alle guten Texteditoren verwenden standardmäßig Tabulatoren (und Definitionen).

  • Ich sage es und ich habe immer Recht!

Leider hört nicht jeder auf die Vernunft. Jemand hat Ihnen eine Datei geschickt, die es falsch macht TM und Sie müssen es reparieren. Du könntest es einfach manuell machen, aber es wird andere geben.

Es ist schon schlimm genug, dass Spacer Ihre kostbare Zeit verschwenden, und Sie entscheiden sich, das kürzestmögliche Programm zu schreiben, um das Problem zu beheben.

Aufgabe

Schreiben Sie ein Programm oder eine Funktion, die Folgendes bewirkt:

  1. Lesen Sie eine einzelne Zeichenfolge entweder aus STDIN oder als Befehlszeilen- oder Funktionsargument.

  2. Identifizieren Sie alle Stellen, an denen Leerzeichen für die Tabellierung oder Einrückung verwendet wurden.

    Ein Leerzeichen ist ein Einzug, wenn er am Anfang einer Zeile steht.

    Eine Folge von zwei oder mehr Leerzeichen ist eine Tabellierung, wenn es sich nicht um Einrückungen handelt.

    Ein einzelnes Leerzeichen, das kein Einzug ist, wurde möglicherweise nicht für die Tabellierung verwendet. Wenn Sie dasselbe Zeichen für verschiedene Zwecke verwenden, gibt es erwartungsgemäß keine einfache Möglichkeit, dies zu erkennen. Deshalb werden wir sagen, dass der Raum für Verwirrung benutzt worden ist .

  3. Bestimmen Sie die längste mögliche Tabulatorbreite 1, für die alle für Tabulatoren oder Einrückungen verwendeten Leerzeichen durch Tabulatoren ersetzt werden können, ohne das Erscheinungsbild der Datei zu ändern.

    Wenn die Eingabe weder Tabulatoren noch Einrückungen enthält, kann die Tabulatorenbreite nicht bestimmt werden. Überspringen Sie in diesem Fall den nächsten Schritt.

  4. Ersetzen Sie unter Verwendung der zuvor festgelegten Tabulatorbreite alle für die Tabellierung oder Einrückung verwendeten Leerzeichen durch Tabulatoren.

    Ersetzen Sie außerdem, wann immer dies möglich ist, ohne das Erscheinungsbild der Datei zu ändern, alle Leerzeichen, die zur Verwechslung verwendet werden, durch Tabulatoren. (Wenn Sie Zweifel haben, entfernen Sie Leerzeichen.)

  5. Geben Sie den geänderten String aus Ihrer Funktion zurück oder drucken Sie ihn an STDOUT.

Beispiele

  • Alle Räume von

    a    bc   def  ghij
    

    sind tabellarisch.

    Bei jedem Durchlauf von Leerzeichen wird die vorhergehende Zeichenfolge mit Nicht-Leerzeichen bis zu einer Breite von 5 aufgefüllt, sodass die richtige Tabulatorbreite 5 und die richtige Ausgabe 2 ist

    a--->bc-->def->ghij
    
  • Die ersten beiden Räume von

    ab  cde f
    ghi jk lm
    

    sind tabellarisch, die anderen verwirrt.

    Die richtige Registerbreite ist 4, die richtige Ausgabe 2 also

    ab->cde>f
    ghi>jk lm
    

    Das letzte Leerzeichen bleibt unangetastet, da es als zwei Leerzeichen dargestellt wird, wenn es durch einen Tabulator ersetzt wird:

    ab->cde>f
    ghi>jk->lm
    
  • Alle bis auf ein Leerzeichen

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

    sind Einrückung, der andere ist Verwirrung.

    Die Einbuchtung Ebene 0, 4 und 8 Felder, so dass die korrekte Tabulatorbreite 4 ist und die korrekte Ausgabe 2 ist

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

    Das Feld in ( )würde als drei Felder dargestellt, wenn es durch einen Tabulator ersetzt würde. Es bleibt also unberührt.

  • Die ersten beiden Räume von

      x yz w
    

    sind Einrückung, die anderen Verwirrung.

    Die richtige Registerbreite ist 2 und die richtige Ausgabe 2 ist

    ->x>yz w
    

    Das letzte Leerzeichen wird als zwei Leerzeichen dargestellt, wenn es durch einen Tabulator ersetzt wird. Es bleibt also unberührt.

  • Die ersten beiden Räume von

      xy   zw
    

    sind Einrückungen, die anderen drei sind Tabellierungen.

    Nur eine Tabulatorbreite von 1 erlaubt es, alle Leerzeichen zu entfernen, sodass die richtige Ausgabe 2 vorliegt

    >>xy>>>zw
    
  • Alle Räume von

    a b c d
    

    sind Verwirrung.

    Es gibt keine möglichst lange Tabulatorbreite, so dass die korrekte Ausgabe 2 ist

    a b c d
    

Zusätzliche Regeln

  • Die Eingabe besteht ausschließlich aus druckbaren ASCII-Zeichen und Zeilenvorschüben.

  • Sie können davon ausgehen, dass maximal 100 Textzeilen und maximal 100 Zeichen pro Zeile vorhanden sind.

  • Wenn Sie STDOUT für die Ausgabe wählen, können Sie einen einzelnen nachgestellten Zeilenvorschub drucken.

  • Es gelten die Standardregeln für .


1 Die Tabulatorbreite ist definiert als der Abstand in Zeichen zwischen zwei aufeinander folgenden Tabulatoren unter Verwendung einer monospaced Schriftart.
2 Die ASCII-Kunstpfeile stellen die Tabulatoren dar, die Stack Exchange nicht ordnungsgemäß rendern kann, für die ich einen Fehlerbericht eingereicht habe. Die eigentliche Ausgabe muss eigentliche Tabulatoren enthalten.

Dennis
quelle
9
+1 für die endgültige Beseitigung dieses unsinnigen Problems mit Leerzeichen / Tabs: D
Geobits
2
programs should be as short as possibleIch glaube, ich habe Arthur Whitneys längst verschollenen Bruder gefunden !!
kirbyfan64sos
13
Tabs sind unheilige Dämonenbrut, die es verdient haben, dass ihre Teile auseinandergerissen und ihr ASCII-Code entehrt werden, bis ihr inkompetenter Seelenmangel gründlich zu Brei zermahlen wurde. Errr, ich meine, +1, nette Herausforderung, obwohl es nach Blasphemie riecht. ;)
Türklinke
1
Ich habe jedes Mal geweint, wenn ein Kollege einen Tabulator in meinen schönen Code eingefügt hat. Dann entdeckte ich STRG + K + F in Visual Studio. Ich mache es jedes Mal, wenn ich eine geänderte Datei öffne. Mein Leben ist jetzt besser.
Michael M.

Antworten:

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

Probieren Sie es online

Interessante Idee, da aber Tabulatoren in der Eingabe das Konzept sprengen, wenig brauchbar.

Bearbeiten: Fehler behoben. vielen dank @aditsu

Brian Tuck
quelle
Es stürzt auf "abc d"
aditsu
@Aditsu Mist! Vielen Dank für das Heads-up. Ich brauche bessere Testfälle: 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"

Ich ging voran und benutzte Zeilenumbrüche anstatt ;wo möglich, um die Anzeige zu vereinfachen. Ich verwende Unix-Zeilenenden, daher sollte dies keinen Einfluss auf die Byteanzahl haben.

Wie man ausführt

Kopieren Sie den Code in die SpaceMadness.ps1Datei und leiten Sie die Eingabe in das Skript. Ich gehe davon aus, dass die zu konvertierende Datei heißt taboo.txt:

Aus PowerShell:

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

Von der Eingabeaufforderung:

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

Ich habe es mit PowerShell 5 getestet, aber es sollte mit 3 oder höher funktionieren.

Testen

Hier ist ein kurzer PowerShell-Scrip, mit dem Sie die oben genannten Funktionen testen können:

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

Legen Sie dies in das gleiche Verzeichnis wie SpaceMadness.ps1, ich nenne es dieses tester.ps1, nennen Sie es wie folgt:

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

Du hast die Idee. Es spuckt den Inhalt jeder Datei nach der Konvertierung aus und durchläuft [RegEx]::Escape()diese, wobei sowohl Leerzeichen als auch Tabulatoren ausgeblendet werden, so dass es sehr praktisch ist, zu sehen, was tatsächlich geändert wurde.

Die Ausgabe sieht folgendermaßen aus (aber mit Farben):

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

Erläuterung

Die allererste Zeile definiert gso kurz wie möglich die größte gemeinsame Faktor / Divisor-Funktion , die ein Array (eine beliebige Anzahl von Zahlen) aufnimmt und GCD rekursiv mit dem euklidischen Algorithmus berechnet .

Ziel war es, die "größtmögliche Tabulatorbreite" zu ermitteln, indem der Index + die Länge aller in der Frage definierten Einrückungen und Tabulatoren genommen und dann dieser Funktion zugeführt wurden, um die GCD zu erhalten, die meines Erachtens die beste ist, die wir können für die Tabulatorbreite tun. Die Länge einer Verwechslung ist immer 1, daher trägt sie nichts zu dieser Berechnung bei.

$bdefiniert einen Scriptblock, weil ich diesen Code ärgerlicherweise zweimal aufrufen muss, um so einige Bytes zu sparen. Dieser Block nimmt den String (oder das Array von Strings) $nund führt einen regulären Ausdruck darauf ( slsoder Select-String) aus, der Übereinstimmungsobjekte zurückgibt. Ich erhalte hier tatsächlich sowohl Einrückungen als auch Tabellierungen in einer, was mir zusätzliche Verarbeitung erspart hat, da ich sie separat erfasst habe.

$nwird für verschiedene Dinge innerhalb und außerhalb der Hauptschleife verwendet (wirklich schlecht, aber hier notwendig, damit ich es in $bden Skriptblock einbetten und das sowohl innerhalb als auch außerhalb der Schleife ohne lange param()Deklaration und Übergabe von Argumenten verwenden kann.

$sDie Tabulatorbreite wird zugewiesen, indem der $bBlock in dem Zeilenarray in der Eingabedatei aufgerufen, dann der Index und die Länge jeder Übereinstimmung summiert und das Array der Summen als Argument in die GCD-Funktion zurückgegeben wird. So $shat die Größe unserer Tabs jetzt aufgehört.

Dann beginnt die Schleife. Wir iterieren über jede Zeile im Array der Eingabezeilen $n. Das erste, was ich in der Schleife mache, ist, $nden Wert der aktuellen Zeile aus dem oben genannten Grund zuzuweisen (lokaler Bereich).

$w Ruft den Wert des Scriptblock-Aufrufs nur für die aktuelle Zeile ab (die Einrückungen und Tabellen für die aktuelle Zeile).

$cbekommt einen ähnlichen Wert, aber stattdessen finden wir alle Verwirrungen .

Ich addiere $wund $cwelche Arrays sind, und gebe mir ein Array mit allen benötigten Speicherplatzübereinstimmungen, sortin absteigender Reihenfolge nach Index, und beginne, über jede Übereinstimmung für die aktuelle Zeile zu iterieren.

Die Art ist wichtig. Schon früh habe ich herausgefunden, wie schwierig es ist, Teile eines Strings basierend auf Indexwerten zu ersetzen, wenn der Ersetzungsstring kleiner ist und die Länge des Strings ändert! Die anderen Indizes werden ungültig. Wenn ich also mit den höchsten Indizes in jeder Zeile beginne, stelle ich sicher, dass ich die Zeichenfolge nur vom Ende kürzer mache und rückwärts gehe, damit die Indizes immer funktionieren.

In dieser Schleife $xbefindet sich im Index der aktuellen Übereinstimmung $ldie Länge der aktuellen Übereinstimmung. $skann in der Tat sein 0und das verursacht einen lästigen Division durch Null Fehler, also überprüfe ich auf seine Gültigkeit und mache dann die Mathematik.

Das !(($x+$l)%$s)Bit dort ist der einzelne Punkt, an dem ich überprüfe, ob eine Verwirrung durch einen Tabulator ersetzt werden soll oder nicht. Wenn der Index plus die durch die Tabulatorbreite geteilte Länge keinen Rest enthält, sollten Sie diese Übereinstimmung durch einen Tabulator ersetzen (diese Mathematik funktioniert immer für die Einrückungen und Tabulatoren , da ihre Größe die Tabulatorbreite bestimmt hat beginnen mit).

Beim Ersetzen wirkt sich jede Iteration der Übereinstimmungsschleife auf die aktuelle Zeile der Eingabe aus, sodass es sich um eine kumulative Menge von Ersetzungen handelt. Der reguläre Ausdruck sucht nur nach $lLeerzeichen, denen ein $xbeliebiges Zeichen vorangestellt ist . Wir ersetzen es durch $l/$sTabulatorzeichen (oder 1, wenn diese Zahl unter Null liegt).

Dieser Teil (($l/$s),1-ge1)[0]ist eine ausgefallene gewundene Redewendung if (($l/$s) -lt 0) { 1 } else { $l/$s }oder alternativ [Math]::Max(1,($l/$s)). Es erstellt ein Array aus $l/$sund 1verwendet dann -ge 1, um ein Array zurückzugeben, das nur die Elemente enthält, die größer oder gleich eins sind, und nimmt dann das erste Element. Es kommt in ein paar Bytes kürzer als die [Math]::MaxVersion.

Sobald alle Ersetzungen abgeschlossen sind, wird die aktuelle Zeile aus der ForEach-Object( %) -Iteration zurückgegeben, und wenn alle zurückgegeben werden (ein Array von festen Zeilen), werden sie -joinmit Zeilenumbrüchen versehen (da wir am Anfang auf Zeilenumbrüche aufgeteilt haben).

Ich habe das Gefühl, dass es hier Raum für Verbesserungen gibt, für die ich zu ausgebrannt bin, aber vielleicht sehe ich etwas später.

Tabs 4 lyfe

Briantist
quelle
4

PHP - 278 210 Bytes

Die Funktion testet jede Registerbreite, beginnend mit dem Wert 100, der maximalen Länge einer Zeile und damit der maximalen Registerbreite.

Für jede Registerbreite teilen wir jede Zeile in "Blöcke" dieser Länge. Für jeden dieser Blöcke:

  • Wenn wir durch Verketten des letzten Zeichens des vorherigen Blocks mit diesem Block zwei aufeinanderfolgende Leerzeichen vor einem Zeichen finden, erhalten wir einen Einzug oder eine Tabelle, die nicht in Leerzeichen umgewandelt werden kann, ohne das Erscheinungsbild zu ändern. wir versuchen die nächste tab breite.
  • Wenn das letzte Zeichen ein Leerzeichen ist, entfernen wir Leerzeichen am Ende des Blocks, fügen einen Tabulator hinzu und merken uns das Ganze.
  • Ansonsten merken wir uns den Block.

Sobald alle Blöcke einer Zeile analysiert wurden, speichern wir einen Zeilenvorschub. Wenn alle Blöcke aller Zeilen erfolgreich analysiert wurden, geben wir die gespeicherte Zeichenfolge zurück. Andernfalls, wenn jede streng positive Tabulatorbreite ausprobiert wurde, gab es weder eine Tabellierung noch eine Einrückung, und wir geben die ursprüngliche Zeichenfolge zurück.

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

Hier ist die ungolfed Version:

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

Besonderer Dank geht an DankMemes für die Einsparung von 2 Bytes.

Schwarzes Loch
quelle
1
Sie können 2 Bytes sparen, indem Sie for($t=101;--$t;)anstelle vonfor($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*

Probieren Sie es online aus

Ich musste diese Herausforderung annehmen, weil ich meinen Teil dazu beitragen musste, die Welt von diesem Gräuel zu befreien. Tabs sind offensichtlich überlegen, aber leider können einige Leute einfach nicht mit ihnen argumentiert werden.

Erläuterung:

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

Probieren Sie es online!

Weniger golfen:

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
quelle