Mój Boże, jest pełno przestrzeni!

42

Niektóre osoby upierają się przy użyciu spacji do tabel i wcięć.

W przypadku tabeli jest to bezsprzecznie złe. Z definicji tabulatory muszą być używane do tworzenia tabel.

Nawet w przypadku wcięć tabulatory są obiektywnie lepsze:

  • Społeczność Stack Exchange ma wyraźny konsensus .

  • Używanie pojedynczej przestrzeni do wcięcia jest wizualnie nieprzyjemne; używanie więcej niż jednego jest marnotrawstwem.

    Ponieważ wszystkie cod e golfowych ers wiedzieć, programy powinny być jak najkrótsze. Nie tylko oszczędza miejsce na dysku twardym, ale także skraca czas kompilacji, jeśli trzeba przetworzyć mniej bajtów.

  • Dostosowując szerokość zakładki 1 , ten sam plik wygląda inaczej na każdym komputerze, dzięki czemu każdy może użyć swojej ulubionej szerokości wcięcia bez modyfikowania rzeczywistego pliku.

  • Wszystkie dobre edytory tekstu domyślnie używają tabulatorów (i definicji).

  • Mówię tak i zawsze mam rację!

Niestety nie wszyscy słuchają rozsądku. Ktoś wysłał ci plik, który robi to źle TM i musisz go naprawić. Możesz to zrobić ręcznie, ale będą też inne.

Na tyle źle, że spacery marnują Twój cenny czas, więc decydujesz się napisać możliwie najkrótszy program, aby zająć się problemem.

Zadanie

Napisz program lub funkcję, która wykonuje następujące czynności:

  1. Odczytaj pojedynczy ciąg znaków ze STDIN lub jako argument wiersza polecenia lub funkcji.

  2. Zidentyfikuj wszystkie lokalizacje, w których spacje zostały użyte do tworzenia tabel lub wcięć.

    Ciąg spacji jest wcięciem, jeśli występuje na początku linii.

    Ciąg dwóch lub więcej spacji jest tabelą, jeśli nie jest wcięciem.

    Pojedynczy przestrzeń, która nie jest wcięcie może być lub nie zostały wykorzystane na zestawianiu. Zgodnie z oczekiwaniami, gdy używasz tej samej postaci do różnych celów, nie ma łatwego sposobu, aby powiedzieć. Dlatego powiemy, że przestrzeń została wykorzystana do zamieszania .

  3. Określ najdłuższą możliwą szerokość tabulatora 1, dla której wszystkie spacje używane do tabulacji lub wcięcia można zastąpić tabulatorami, bez zmiany wyglądu pliku.

    Jeśli dane wejściowe nie zawierają tabulacji ani wcięcia, nie można określić szerokości tabulacji. W takim przypadku pomiń następny krok.

  4. Używając wcześniej określonej szerokości tabulacji, zamień wszystkie spacje używane do tabelowania lub wcięcia na tabulatory.

    Ponadto, o ile to możliwe, bez zmiany wyglądu pliku, zamień wszystkie spacje używane do pomyłek na tabulatory. (W razie wątpliwości pozbądź się spacji).

  5. Zwróć zmodyfikowany ciąg z funkcji lub wydrukuj go do STDOUT.

Przykłady

  • Wszystkie spacje z

    a    bc   def  ghij
    

    są tabelaryczne.

    Każdy ciąg spacji dopełnia poprzedzający ciąg znaków spacji do szerokości 5, więc poprawna szerokość tabulacji wynosi 5, a poprawne wyjście 2 to

    a--->bc-->def->ghij
    
  • Pierwsze dwie spacje z

    ab  cde f
    ghi jk lm
    

    to tabulacja, inne zamieszanie.

    Prawidłowa szerokość zakładki wynosi 4, więc poprawna wartość wyjściowa 2 to

    ab->cde>f
    ghi>jk lm
    

    Ostatnia spacja pozostaje nietknięta, ponieważ byłaby renderowana jako dwie spacje, gdyby została zastąpiona tabulatorem:

    ab->cde>f
    ghi>jk->lm
    
  • Wszystkie oprócz jednego miejsca

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

    są wcięcia, drugie to zamieszanie.

    Poziomy wcięcia wynoszą 0, 4 i 8 spacji, więc poprawna szerokość tabulacji wynosi 4, a poprawna wartość wyjściowa 2 to

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

    Spacja w ( )byłaby renderowana jako trzy spacje, gdyby została zastąpiona tabulatorem, więc pozostaje nietknięta.

  • Pierwsze dwie spacje z

      x yz w
    

    są wcięcia, inne zamieszanie.

    Prawidłowa szerokość zakładki wynosi 2, a poprawna wartość wyjściowa 2 to

    ->x>yz w
    

    Ostatnia spacja byłaby renderowana jako dwie spacje, gdyby została zastąpiona tabulatorem, więc pozostaje nietknięta.

  • Pierwsze dwie spacje z

      xy   zw
    

    są wcięcia, pozostałe trzy to tabulacje.

    Tylko szerokość tabulatora 1 pozwala wyeliminować wszystkie spacje, więc poprawne wyjście 2 to

    >>xy>>>zw
    
  • Wszystkie spacje z

    a b c d
    

    są zamieszaniem.

    Nie ma najdłuższej możliwej szerokości tabulacji, więc poprawne wyjście 2 to

    a b c d
    

Dodatkowe zasady

  • Dane wejściowe będą składały się wyłącznie z drukowalnych znaków ASCII i linii.

  • Możesz założyć, że w wierszu jest maksymalnie 100 wierszy tekstu i maksymalnie 100 znaków.

  • Jeśli wybierzesz STDOUT jako wyjście, możesz wydrukować jedno końcowe podawanie linii.

  • Obowiązują standardowe zasady .


1 Szerokość tabulacji jest definiowana jako odległość w znakach między dwoma kolejnymi tabulatorami przy użyciu czcionki o stałej szerokości .
2 Strzałki graficzne ASCII przedstawiają tabulatory Stack Exchange odmawia poprawnego renderowania, dla którego przesłałem raport o błędzie. Rzeczywiste wyjście musi zawierać rzeczywiste tabulatory.

Dennis
źródło
9
+1 za wreszcie odłożenie tego bezsensownego problemu ze spacją / tabulatorem: D
Geobits
2
programs should be as short as possibleWydaje mi się, że znalazłem dawno zaginionego brata Artura Whitneya !!
kirbyfan64sos
13
Karty to bezbożne demoniczne pomiot, które zasługują na rozerwanie ich bitów i zhańbienie ich kodu ASCII, dopóki ich niekompetentny brak duszy nie zostanie dokładnie zmielony na miazgę. Errr, mam na myśli +1, niezłe wyzwanie, choć cuchnie bluźnierstwem. ;)
Klamka
1
Płakałem za każdym razem, gdy kolega dodawał zakładkę do mojego pięknego kodu z wcięciami w przestrzeni. Potem odkryłem CTRL + K + F w Visual Studio. Robię to za każdym razem, gdy otwieram zmodyfikowany plik. Moje życie jest teraz lepsze.
Michael M.

Odpowiedzi:

5

Pyth, 102 103 bajty

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

Wypróbuj online

Ciekawy pomysł, ale ponieważ zakładki na wejściu łamią koncepcję, niezbyt użyteczną.

Edycja: Naprawiono błąd. wielkie dzięki @aditsu

Brian Tuck
źródło
Rozbija się
@aditsu crap! Dzięki za heads-up. Potrzebuję lepszych przypadków testowych: P
Brian Tuck,
5

PowerShell, 414 409 bajtów

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"

Poszedłem dalej i użyłem nowego wiersza zamiast, ;gdzie to możliwe, aby ułatwić wyświetlanie. Używam końcówek linii uniksowych, więc nie powinno to wpływać na liczbę bajtów.

Jak wykonać

Skopiuj kod do SpaceMadness.ps1pliku, a następnie potokuj dane wejściowe do skryptu. Zakładam, że plik wymagający konwersji nazywa się taboo.txt:

Z PowerShell:

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

Z wiersza polecenia:

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

Testowałem to z PowerShell 5, ale powinno działać na 3 lub wyższej wersji.

Testowanie

Oto krótki skrypt PowerShell, który jest przydatny do testowania powyższego:

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

Umieść to w tym samym katalogu, co SpaceMadness.ps1ja nazywam ten tester.ps1, nazwij to tak:

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

Masz pomysł. Po zakończeniu konwersji wyrzuca zawartość każdego pliku, przez [RegEx]::Escape()który przechodzi przez spacje i tabulatory, więc naprawdę wygodnie jest zobaczyć, co zostało zmienione.

Dane wyjściowe wyglądają tak (ale z kolorami):

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

Wyjaśnienie

Pierwszy wiersz definiuje największą wspólną funkcję czynnik / dzielnik gtak zwięźle, jak potrafiłem, która przyjmuje tablicę (dowolną liczbę liczb) i oblicza rekurencyjnie GCD przy użyciu algorytmu euklidesowego .

Celem tego było ustalenie „najdłuższej możliwej szerokości tabulatora” poprzez pobranie indeksu + długości każdego wcięcia i tabulacji zgodnie z definicją w pytaniu, a następnie przekazanie go do tej funkcji, aby uzyskać GCD, który moim zdaniem jest najlepszy, jaki możemy zrobić dla szerokości tabulacji. Długość pomyłki zawsze będzie wynosić 1, więc nie wnosi nic do tego obliczenia.

$bdefiniuje blok skryptu, ponieważ irytujące muszę dwukrotnie wywołać ten fragment kodu, więc oszczędzam w ten sposób niektóre bajty. Ten blok pobiera ciąg (lub tablicę ciągów) $ni uruchamia na nim wyrażenie regularne ( slslub Select-String), zwracając obiekty dopasowania. Dostaję zarówno wcięcia, jak i tabulacje w jednym tutaj, co naprawdę zaoszczędziło mi dodatkowego przetwarzania, przechwytując je osobno.

$njest używany do różnych rzeczy wewnątrz i na zewnątrz głównej pętli (naprawdę źle, ale jest to konieczne tutaj, aby móc osadzić ją w $bbloku skryptowym i używać zarówno wewnątrz, jak i na zewnątrz pętli bez długiej param()deklaracji i przekazywania argumentów.

$szostaje przypisana szerokość tabulatora, wywołując $bblok na tablicy wierszy w pliku wejściowym, a następnie sumując indeks i długość każdego dopasowania, zwracając tablicę sum jako argument do funkcji GCD. Tak $sma wielkość naszej karcie zatrzymuje teraz.

Następnie rozpoczyna się pętla. Iterujemy po każdej linii w tablicy linii wejściowych $n. Pierwszą rzeczą, którą robię w pętli, jest przypisanie $n(zakres lokalny) wartości bieżącej linii z powyższego powodu.

$w pobiera wartość wywołania scriptblock tylko dla bieżącej linii (wcięcia i tabulacje dla bieżącej linii).

$cotrzymuje podobną wartość, ale zamiast tego znajdujemy wszystkie zamieszania .

Sumuję $wi $cktóre są tablicami, dając mi jedną tablicę ze wszystkimi potrzebnymi dopasowaniami spacji, sortw kolejności malejącej według indeksu i rozpoczynam iterację po każdym dopasowaniu dla bieżącej linii.

Rodzaj jest ważny. Na początku dowiedziałem się na własnej skórze, że zastępowanie części ciągu na podstawie wartości indeksu jest złym pomysłem, gdy zastępowany ciąg jest mniejszy i zmienia długość łańcucha! Pozostałe indeksy zostają unieważnione. Rozpoczynając od najwyższych indeksów w każdym wierszu, upewniam się, że zmniejszam ciąg tylko od końca i przesuwam się do tyłu, aby indeksy zawsze działały.

W tej pętli $xznajduje się w indeksie bieżącego dopasowania i $ljest długością bieżącego dopasowania. $smoże być 0i to powoduje brzydki podział przez zero, więc sprawdzam jego poprawność, a następnie wykonuję matematykę.

Chodzi !(($x+$l)%$s)o jeden punkt, w którym sprawdzam, czy zamieszanie należy zastąpić tabulatorem, czy nie. Jeśli indeks plus długość podzielona przez szerokość tabulatora nie ma już reszty, dobrze jest zastąpić to dopasowanie tabulatorem (matematyka zawsze będzie działać na wcięcia i tabulacje , ponieważ ich rozmiar określa szerokość tabulatora) najpierw).

W przypadku zamiany każda iteracja pętli dopasowania działa w bieżącym wierszu danych wejściowych, więc jest to skumulowany zestaw zamienników. Wyrażenie regularne szuka $lspacji poprzedzonych $xdowolną postacią. Zastępujemy go $l/$sznakami tabulacji (lub 1, jeśli liczba ta jest mniejsza od zera).

Ta część (($l/$s),1-ge1)[0]jest fantazyjnym, skomplikowanym sposobem powiedzenia if (($l/$s) -lt 0) { 1 } else { $l/$s }lub alternatywnie [Math]::Max(1,($l/$s)). To sprawia, że tablicę $l/$si 1, a następnie wykorzystuje -ge 1do powrotu tablicę zawierającą tylko te elementy, które są większe niż lub równe jeden, wówczas wybiera pierwszy element. Występuje w kilku bajtach krótszych niż [Math]::Maxwersja.

Więc kiedy wszystkie zamiany zostaną wykonane, bieżąca linia jest zwracana z iteracji ForEach-Object( %), a kiedy wszystkie są zwracane (tablica linii stałych), jest -joinedytowana z nowymi liniami (ponieważ na początku dzielimy na nowe linie).

Wydaje mi się, że jest tu miejsce na ulepszenia, które jestem teraz zbyt wypalony, aby je złapać, ale może zobaczę coś później.

Tabs 4 lyfe

briantist
źródło
4

PHP - 278 210 bajtów

Funkcja działa poprzez przetestowanie każdej szerokości tabulatora, zaczynając od wartości 100, maksymalnej długości linii, a tym samym maksymalnej szerokości tabulatora.

Dla każdej szerokości tabulatora dzielimy każdą linię na „bloki” o tej długości. Dla każdego z tych bloków:

  • Jeśli, łącząc ostatni znak poprzedniego bloku z tym blokiem, znajdziemy dwa kolejne spacje przed znakiem, mamy wcięcie lub tabulację, których nie można przekształcić w spację bez zmiany wyglądu; próbujemy szerokość następnej zakładki.
  • W przeciwnym razie, jeśli ostatnim znakiem jest spacja, usuwamy spacje na końcu bloku, dodajemy tabulator i zapamiętujemy całość.
  • W przeciwnym razie po prostu zapamiętujemy blok.

Po przeanalizowaniu każdego bloku linii zapamiętujemy wysuw linii. Jeśli wszystkie bloki wszystkich linii zostały z powodzeniem przeanalizowane, zwracamy zapamiętany ciąg. W przeciwnym razie, jeśli wypróbowano każdą ściśle dodatnią szerokość tabulatora, nie było ani tabulacji, ani wcięcia, i zwracamy oryginalny ciąg.

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

Oto wersja bez golfa:

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

Specjalne podziękowania dla DankMemes za zapisanie 2 bajtów.

Czarna dziura
źródło
1
Możesz zapisać 2 bajty, używając for($t=101;--$t;)zamiastfor($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*

Wypróbuj online

Musiałem odpowiedzieć na to wyzwanie, ponieważ muszę zrobić wszystko, aby pomóc uwolnić świat od tej obrzydliwości. Karty są oczywiście lepsze, ale niestety, niektórzy ludzie nie mogą być uzasadnieni.

Wyjaśnienie:

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
źródło
1

PowerShell , 165 160 153 152 142 142 138 137 bajtów

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

Wypróbuj online!

Mniej golfa:

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
źródło