Mon dieu, c'est plein d'espaces!

42

Certaines personnes insistent pour utiliser des espaces pour la tabulation et l'indentation.

Pour la tabulation, c'est indéniablement faux. Par définition, les tabulateurs doivent être utilisés pour la tabulation.

Même pour l’indentation, les tabulateurs sont objectivement supérieurs:

  • Il existe un consensus clair dans la communauté Stack Exchange.

  • L'utilisation d'un seul espace pour l'indentation est visuellement désagréable; en utiliser plus d'un est un gaspillage.

    Comme tous les golfeurs le savent, les programmes doivent être aussi courts que possible. Non seulement cela économise de l'espace sur le disque dur, mais les temps de compilation sont également réduits si moins d'octets doivent être traités.

  • En ajustant la largeur de tabulation 1 , le même fichier a un aspect différent sur chaque ordinateur, de sorte que tout le monde peut utiliser sa largeur d’indentation préférée sans modifier le fichier lui-même.

  • Tous les bons éditeurs de texte utilisent les tabulateurs par défaut (et leur définition).

  • Je le dis et j'ai toujours raison!

Malheureusement, tout le monde n’écoute pas sa raison. Quelqu'un vous a envoyé un fichier qui ne fonctionne pas correctement et vous devez le réparer. Vous pouvez le faire manuellement, mais il y en aura d'autres.

Il est déjà assez grave que les entretoises vous fassent perdre votre temps précieux. Vous décidez donc de rédiger le programme le plus court possible pour résoudre le problème.

Tâche

Ecrivez un programme ou une fonction qui effectue les tâches suivantes:

  1. Lit une seule chaîne de STDIN ou sous forme d'argument de ligne de commande ou de fonction.

  2. Identifiez tous les emplacements où des espaces ont été utilisés pour la tabulation ou l'indentation.

    Une série d'espaces est une indentation si elle se produit au début d'une ligne.

    Une série de deux espaces ou plus est une tabulation si ce n'est pas une indentation.

    Un seul espace qui n'est pas indenté peut ou peut ne pas avoir été utilisé pour la tabulation. Comme prévu lorsque vous utilisez le même personnage à des fins différentes, il n’ya pas de moyen facile de le savoir. Par conséquent, nous dirons que l'espace a été utilisé pour la confusion .

  3. Déterminez la largeur de tabulation 1 la plus longue possible pour laquelle tous les espaces utilisés pour la tabulation ou l'indentation peuvent être remplacés par des tabulateurs, sans modifier l'apparence du fichier.

    Si l'entrée ne contient ni tabulation, ni indentation, il est impossible de déterminer la largeur de la tabulation. Dans ce cas, ignorez l'étape suivante.

  4. En utilisant la largeur de tabulation précédemment déterminée, remplacez tous les espaces utilisés pour la tabulation ou l'indentation par des tabulatrices.

    En outre, chaque fois que cela est possible sans modifier l'apparence du fichier, remplacez tous les espaces utilisés pour confusion par des tabulateurs. (En cas de doute, supprimez les espaces.)

  5. Renvoie la chaîne modifiée à partir de votre fonction ou imprimez-la dans STDOUT.

Exemples

  • Tous les espaces de

    a    bc   def  ghij
    

    sont tabulation.

    Chaque série d'espaces complète la chaîne précédente de caractères autres que des espaces à une largeur de 5, de sorte que la largeur de tabulation correcte est 5 et que la sortie correcte 2 soit

    a--->bc-->def->ghij
    
  • Les deux premiers espaces de

    ab  cde f
    ghi jk lm
    

    sont la tabulation, les autres confusion.

    La largeur de tabulation correcte est 4, la sortie correcte 2 est donc

    ab->cde>f
    ghi>jk lm
    

    Le dernier espace reste inchangé, car il serait rendu comme deux espaces s'il était remplacé par une tabulatrice:

    ab->cde>f
    ghi>jk->lm
    
  • Tous les espaces sauf un de

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

    sont l'indentation, l'autre est la confusion.

    Les niveaux d’indentation sont de 0, 4 et 8 espaces. La largeur de tabulation correcte est donc 4 et la sortie correcte 2 est

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

    L'espace ( )dedans serait rendu comme trois espaces s'il était remplacé par une tabulatrice, de sorte qu'il reste inchangé.

  • Les deux premiers espaces de

      x yz w
    

    sont indentation, les autres confusion.

    La largeur de l'onglet appropriée est 2 et la sortie correcte 2 est

    ->x>yz w
    

    Le dernier espace serait rendu sous forme de deux espaces s'il était remplacé par une tabulatrice, de sorte qu'il reste inchangé.

  • Les deux premiers espaces de

      xy   zw
    

    sont indentation, les trois autres sont tabulation.

    Seule une largeur de tabulation de 1 permet d’éliminer tous les espaces. La sortie 2 correcte est

    >>xy>>>zw
    
  • Tous les espaces de

    a b c d
    

    sont la confusion.

    Il n’ya pas de largeur de tabulation la plus longue possible, la sortie 2 correcte est

    a b c d
    

Règles supplémentaires

  • L'entrée consistera entièrement en caractères ASCII imprimables et sauts de ligne.

  • Vous pouvez supposer qu'il y a au maximum 100 lignes de texte et au plus 100 caractères par ligne.

  • Si vous choisissez STDOUT pour la sortie, vous pouvez imprimer un seul saut de ligne de fin.

  • Les règles standard de s'appliquent.


1 La largeur de tabulation est définie comme la distance en caractères entre deux points de tabulation consécutifs , en utilisant une police à espacement fixe.
2 Les flèches art ASCII représentent les tabulateurs que Stack Exchange refuse de restituer correctement, pour lequel j'ai soumis un rapport de bogue. La sortie réelle doit contenir des tabulateurs réels.

Dennis
la source
9
+1 pour avoir enfin résolu ce problème absurde d'espace / tabulation: D
Geobits
2
programs should be as short as possibleJe crois avoir retrouvé le frère longtemps perdu d'Arthur Whitney !!
kirbyfan64sos
13
Les onglets sont des créatures impies qui méritent d'avoir leurs morceaux déchirés et leur code ASCII déshonoré jusqu'à ce que leur manque d'âme incompétente soit complètement réduit en miettes. Euh, je veux dire, +1, beau défi, même s'il sent le blasphème. ;)
Poignée de porte
1
Je pleurais à chaque fois qu'un collègue ajoutait un onglet dans mon bel espace indenté. Ensuite, j'ai découvert CTRL + K + F dans Visual Studio. Je le fais chaque fois que j'ouvre un fichier modifié. Ma vie est meilleure maintenant.
Michael M.

Réponses:

5

Pyth, 102 103 octets

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

Essayez-le en ligne

Idée intéressante, mais comme les onglets de l’entrée cassent le concept, ce n’est pas très utilisable.

Edit: Correction d'un bug. merci beaucoup @aditsu

Brian Tuck
la source
Il se bloque sur "abc d"
aditsu
@aditsu merde! Merci pour le heads-up. J'ai besoin de meilleurs cas de tests: P
Brian Tuck
5

PowerShell, 414 409 octets

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"

Je suis allé de l'avant et j'ai utilisé les nouvelles lignes au lieu de, ;lorsque cela était possible, pour faciliter l'affichage. J'utilise des fins de ligne Unix, cela ne devrait donc pas affecter le nombre d'octets.

Comment exécuter

Copiez le code dans un SpaceMadness.ps1fichier, puis transmettez l’entrée au script. Je supposerai que le fichier à convertir s'appelle taboo.txt:

De PowerShell:

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

A partir de l'invite de commande:

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

Je l'ai testé avec PowerShell 5, mais il devrait fonctionner à 3 ou plus.

Essai

Voici un bref script PowerShell utile pour tester ce qui précède:

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

Mettez ceci dans le même répertoire que SpaceMadness.ps1, j'appelle celui-ci tester.ps1, appelez-le comme suit:

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

Vous avez eu l'idée. Il convertit le contenu de chaque fichier après la conversion, puis [RegEx]::Escape()passe à travers ce qui échappe à la fois aux espaces et aux tabulations. Il est donc très pratique de voir ce qui a réellement été modifié.

La sortie ressemble à ceci (mais avec des couleurs):

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

Explication

La toute première ligne définit la plus grande fonction commune facteur / diviseur gaussi succinctement que je puisse gérer, qui prend un tableau (nombre arbitraire de nombres) et calcule GCD de manière récursive en utilisant l' algorithme Euclidien .

Le but de ceci était de déterminer la "plus grande largeur de tabulation possible" en prenant l'index + la longueur de chaque indentation et tabulation telle que définie dans la question, puis en l'insérant dans cette fonction pour obtenir le GCD qui, à mon avis, est le meilleur possible. faire pour la largeur de l'onglet. La longueur d'une confusion sera toujours égale à 1, elle ne contribue donc pas à ce calcul.

$bdéfinit un scriptblock parce que, de manière ennuyeuse, j'ai besoin d'appeler ce morceau de code deux fois, donc je sauvegarde des octets de cette façon. Ce bloc prend la chaîne (ou le tableau de chaînes) $net exécute une expression rationnelle dessus ( slsou Select-String), renvoyant les objets correspondants. En fait, je reçois les indentations et les tableaux dans un, ce qui m'a vraiment évité un traitement supplémentaire en les capturant séparément.

$nest utilisé pour différentes choses à l'intérieur et à l'extérieur de la boucle principale (vraiment mauvais, mais nécessaire ici pour que je puisse l'intégrer dans $ble scriptblock et l'utiliser à la fois à l'intérieur et à l'extérieur de la boucle sans une param()déclaration longue et des arguments de passage.

$sse voit attribuer la largeur de tabulation, en appelant le $bbloc sur le tableau de lignes du fichier d'entrée, puis en additionnant l'index et la longueur de chaque correspondance, en renvoyant le tableau des sommes sous forme d'argument dans la fonction GCD. La $staille de notre onglet s'arrête maintenant.

Puis la boucle commence. Nous parcourons chaque ligne du tableau de lignes en entrée $n. La première chose que je fais dans la boucle est d’attribuer $n(portée locale) la valeur de la ligne en cours pour la raison ci-dessus.

$w obtient la valeur de l'appel de script pour la ligne en cours uniquement (les indentations et les tabulations pour la ligne en cours).

$cobtient une valeur similaire, mais à la place, nous trouvons toutes les confusions .

J'additionne $wet $cqui sont des tableaux, me donnant un tableau avec toutes les correspondances d'espace dont j'ai besoin, sorten ordre décroissant par index, et commence à itérer sur chaque correspondance pour la ligne en cours.

Le genre est important. J'ai découvert très tôt qu'il était difficile de remplacer des parties d'une chaîne en fonction de valeurs d'index lorsque la chaîne de remplacement est plus petite et modifie la longueur de la chaîne! Les autres index sont invalidés. Donc, en commençant par les index les plus élevés sur chaque ligne, je m'assure de ne raccourcir que la chaîne et de revenir en arrière pour que les index fonctionnent toujours.

Dans cette boucle, se $xtrouve dans l'index de la correspondance actuelle et $lcorrespond à la longueur de la correspondance actuelle. $speut en fait être 0et cela cause une division embarrassante par zéro erreur donc je vérifie sa validité puis fais le calcul.

Le !(($x+$l)%$s)bit il y a le seul point où je vérifie si une confusion doit être remplacée par un onglet ou non. Si l'index plus la longueur divisée par la largeur de la tabulation n'a pas de reste, nous pouvons remplacer cette correspondance par une tabulation (cette mathématique fonctionnera toujours sur les empreintes et les tabulations , car c'est leur taille qui a déterminé la largeur de la tabulation. pour commencer).

Pour le remplacement, chaque itération de la boucle de correspondance fonctionne sur la ligne en cours de l'entrée. Il s'agit donc d'un ensemble cumulatif de remplacements. La regex ne cherche que les $lespaces précédés $xde n'importe quel caractère. Nous le remplaçons par des $l/$scaractères de tabulation (ou 1 si ce nombre est inférieur à zéro).

Cette partie (($l/$s),1-ge1)[0]est une manière compliquée de dire if (($l/$s) -lt 0) { 1 } else { $l/$s }ou d’alternative [Math]::Max(1,($l/$s)). Il crée un tableau de $l/$set 1, utilise ensuite -ge 1pour renvoyer un tableau contenant uniquement les éléments supérieurs ou égaux à un, puis prend le premier élément. Il vient dans quelques octets plus courts que la [Math]::Maxversion.

Ainsi, une fois que tous les remplacements sont terminés, la ligne actuelle est renvoyée par l' itération ForEach-Object( %) et lorsque tous sont renvoyés (un tableau de lignes fixes), elle est -joinéditée avec des nouvelles lignes (puisque nous nous sommes séparés au début des lignes).

J'ai l'impression qu'il y a place à l'amélioration ici et que je suis trop épuisé pour pouvoir l'attraper maintenant, mais je verrai peut-être quelque chose plus tard.

Onglets 4 lyfe

briantiste
la source
4

PHP - 278 210 octets

La fonction teste chaque largeur de tabulation, en commençant par la valeur 100, la longueur maximale d’une ligne et donc la largeur maximale de la tabulation.

Pour chaque largeur de tabulation, nous divisons chaque ligne en "blocs" de cette longueur. Pour chacun de ces blocs:

  • Si, en concaténant le dernier caractère du bloc précédent avec ce bloc, nous trouvons deux espaces consécutifs avant un caractère, nous obtenons une indentation ou une tabulation qui ne peut pas être transformée en espace sans modifier l'apparence; nous essayons la prochaine largeur de l'onglet.
  • Sinon, si le dernier caractère est un espace, nous supprimons des espaces à la fin du bloc, ajoutons une tabulatrice et mémorisons le tout.
  • Sinon, nous ne faisons que mémoriser le bloc.

Une fois que chaque bloc d’une ligne a été analysé, nous mémorisons un saut de ligne. Si tous les blocs de toutes les lignes ont été analysés avec succès, nous renvoyons la chaîne que nous avons mémorisée. Sinon, si chaque largeur de tabulation strictement positive a été essayée, il n'y avait ni tabulation, ni indentation, et nous renvoyons la chaîne d'origine.

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

Voici la version non-golfée:

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 merci spécial à DankMemes pour la sauvegarde de 2 octets.

Trou noir
la source
1
Vous pouvez économiser 2 octets en utilisant à la for($t=101;--$t;)place defor($t=100;$t;--$t)
DankMemes le
4

CJam, 112

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

Essayez-le en ligne

Je devais répondre à ce défi, car je dois faire ma part pour aider à débarrasser le monde de cette abomination. Les onglets sont évidemment supérieurs, mais malheureusement, certaines personnes ne peuvent tout simplement pas être raisonnées.

Explication:

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
la source
1

PowerShell , 165 160 153 152 142 142 138 137 octets

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

Essayez-le en ligne!

Moins joué au 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
mazzy
la source