Conseils pour jouer au golf à Sed


19

Quels conseils généraux avez-vous pour jouer au golf à Sed? Je recherche des idées qui peuvent être appliquées aux problèmes de code-golf et qui sont également au moins quelque peu spécifiques à sed (par exemple, "supprimer les commentaires" n'est pas une réponse).

Veuillez poster un pourboire par réponse.


4
Pas vraiment une astuce de golf (mais toujours une astuce pour le golf): les sauts de ligne consomment autant d'octets que de points-virgules, vous pouvez donc garder votre code court et lisible.
Dennis

Pas un conseil non plus, mais un problème: j'ai GNU sed, mais la Fcommande n'a jamais fonctionné. Quelqu'un sait-il pourquoi?
seshoumara

@seshoumara Ffonctionne sur mon GNU sed (test Debian). Il s'imprime juste -si vous lisez depuis stdin, bien sûr, mais c'est prévu. De quoi tirez-vous sed -e 'F;Q' /etc/hostname?
Toby Speight

@TobySpeight Cela donne cette erreur: char 1: unknown command: F. Je dois mettre à jour sed peut-être; Quelle version avez-vous? La Lcommande ne fonctionne pas non plus, mais elle est de toute façon inutile depuis qu'elle -l nexiste. Tout le reste mentionné sur le site de GNU sed fonctionne.
seshoumara

1
J'ai ouvert la salle de chat bash, sed and dcpour tous ceux qui veulent parler et poser des questions sur ces langues. Faisons une communauté!
seshoumara

Réponses:


11

Si vous devez utiliser des étiquettes, vous voudrez certainement que les noms de vos étiquettes soient aussi courts que possible. En fait, poussé à l'extrême, vous pouvez même utiliser la chaîne vide comme nom d'étiquette:

:    # define label ""
p    # print pattern space
b    # infinite loop! - branch to label ""

4
Depuis gnu sed 4.3, ce comportement a été supprimé . :nécessite désormais une étiquette.
Kevin

En effet, voici également le lien de validation de git . Je suppose que pour PPCG, cela ne changera pas grand-chose, car nous sommes autorisés à publier des réponses pour GNU sed 4.2.x, mais il est bon de savoir, mais malheureusement, que cette astuce ne fonctionnera plus officiellement.
seshoumara

8

La documentation GNU sed décrit la scommande comme "le couteau suisse de sed" . Mais si tout ce que vous voulez faire est de remplacer toutes les instances d'un caractère par un autre, alors la ycommande est ce dont vous avez besoin:

y/a/b/

est un caractère plus court que:

s/a/b/g

son aussi beaucoup plus rapide, et peut échanger des caractères en place:y/12/21/
mikeserv

6

Pensez à utiliser la syntaxe regex étendue (dans GNU sed). L' -roption coûte un octet en notation, mais l'utiliser une seule fois pour éliminer les barres obliques inverses d'une paire \(...\)a déjà payé.


2
Avec la note supplémentaire qui -rsemble être sedspécifique à GNU .
manatwork

@manat - ajouté (mais c'est une réponse du wiki communautaire, vous auriez donc pu vous éditer).
Toby Speight

Bien sûr. Je ne l'ai tout simplement pas considéré comme faisant partie de l'astuce, seulement une note supplémentaire.
manatwork

Et il continue à payer pour lui - même lors de l' utilisation +, ?, {}et |dans les matches regex, car aucune barre oblique inverse sont nécessaires non plus .
seshoumara

-Efonctionne comme un alias -rdans de nombreuses sedimplémentations si je me souviens bien.
phk

6

Lors d'un remplacement répété dans une boucle:

loop:
s/foo/bar/g
tloop

il n'est généralement pas nécessaire de le remplacer globalement, car la boucle finira par remplacer toutes les occurrences:

# GNU sed
:
s/foo/bar/
t

Notez également l'extension GNU ci-dessus: une étiquette peut avoir un nom vide, économisant ainsi des octets plus précieux. Dans d'autres implémentations, une étiquette ne peut pas être vide et le saut sans étiquette transfère le flux à la fin du script (c'est-à-dire le même que n).


1
Le nom d'étiquette vide est spécifique à GNU, POSIX nécessite des branches sans argument pour passer à la fin du script (semble être le comportement dans les BSD et Busybox, également dans GNU sed si vous n'ajoutez pas de vide :)
ninjalj

2
L'étiquette sans nom a toujours été un bogue dans GNU sed, pas une extension, et dans la version 4.3 et supérieure, ce bogue a malheureusement été corrigé. Voyez ici .
seshoumara

5

Il n'y a pas d'arithmétique intégrée, mais les calculs peuvent être effectués en décimal unaire ou codé unaire. Le code suivant convertit la décimale en UCD, avec x comme unité et 0 comme séparateur de chiffres:

s/[1-9]/0&/g
s/[5-9]/4&/g
y/8/4/
s/9/4&/g
s/4/22/g
s/[37]/2x/g
s/[26]/xx/g
s/[1-9]/x/g

et voici la conversion en décimal:

s/0x/-x/g
s/xx/2/g
y/x/1/
s/22/4/g
s/44/8/g
s/81/9/g
s/42/6/g
s/21/3/g
s/61/7/g
s/41/5/g
s/-//g

Ceux-ci sont tous deux tirés d' une réponse à "Multipliez deux nombres sans utiliser de nombres" .

Unaire ancien simple peut être converti en utilisant cette paire de boucles de cette réponse en "{nombres bouclés};" , où se trouve l'unité ;. J'ai utilisé vet xpour correspondre à Roman pour 5et 10; bvient de "bis".

# unary to decimal
:d
/;/{
s/;;;;;/v/g
s/vv/x/g
/[;v]/!s/x\+/&0/
s/;;/b/g
s/bb/4/
s/b;/3/
s/v;/6/
s/vb/7/
s/v3/8/
s/v4/9/
y/;bvx/125;/
td
}

# Decimal to unary
:u
s/\b9/;8/
s/\b8/;7/
s/\b7/;6/
s/\b6/;5/
s/\b5/;4/
s/\b4/;3/
s/\b3/;2/
s/\b2/;1/
s/\b1/;0/
s/\b0//
/[^;]/s/;/&&&&&&&&&&/g
tu

1
... et si vous devez utiliser l'un ou l'autre, vous avez presque certainement déjà perdu le code golf, bien que vous puissiez toujours être compétitif avec les réponses Java ;-) Toujours aussi amusant à utiliser.
Digital Trauma

La conversion de unaire simple en décimal donne de mauvaises réponses pour l'équivalent d'entrée unaire de la forme décimale X0X, par exemple 108. La ligne responsable de cela est /[;v]/!s/\b/0/2, qui doit être changée en /[;v]/!s:x\+:&0:pour qu'elle fonctionne. Voir ici .
seshoumara

@seshoumara, votre lien semble être une page vide. Mais il est tout à fait plausible que j'ai fait une erreur lors de l'extraction de ce code à partir de la réponse référencée, je vais donc simplement appliquer votre correctif.
Toby Speight

Le lien se charge correctement, mais je m'attendais à autre chose qu'une page grise avec "TIO" et quelque chose qui ressemble au logo Ubuntu - est-ce que c'est prévu? Et je faisais référence à la deuxième des réponses auxquelles j'ai fait référence ( 58007 ), car c'est de là que provient l'échantillon unitaire.
Toby Speight du

La liaison TIO aurait dû contenir le code corrigé, plus un exemple d'entrée, 108 en unaire. Lors de l'exécution du code, vous devriez avoir vu le résultat correct 108, et non 180, comme précédemment généré par cette ligne de code désormais fixe. La mise à jour de la réponse référencée dépend entièrement de vous. Ceci est un wiki communautaire.
seshoumara

4

Comme mentionné dans man sed(GNU), vous pouvez utiliser n'importe quel caractère comme délimiteur pour les expressions régulières en utilisant la syntaxe

\%regexp%

%est un espace réservé pour n'importe quel caractère.

Ceci est utile pour des commandes comme

/^http:\/\//

qui sont plus courts que

\%^http://%

Ce qui est mentionné dans le manuel GNU sed, mais pas dans, man sedc'est que vous pouvez également changer les délimiteurs de s///et y///.

Par exemple, la commande

ss/ssg

supprime toutes les barres obliques de l'espace de motif.


4

S'il n'est pas explicitement interdit par la question, le consensus pour cette méta-question est que l'entrée numérique peut être unaire. Cela vous permet d'économiser les 86 octets décimaux à unaire selon cette réponse .


N'est-ce pas un méta consensus pour sed se référant à un vieux format unaire? J'ai plusieurs réponses où une entrée dans UCD pourrait m'aider, au cas où ce serait dans les deux sens.
seshoumara

@seshoumara Je voulais dire unaire, pas UCD
Digital Trauma

Ensuite, la conversion de décimal en unaire ancien simple vous permet d'économiser 126 octets selon la réponse que vous avez liée. Les 86 octets sont destinés à la conversion en UCD.
seshoumara

4

En développant cette réponse de pointe , concernant les conversions entre les formats de nombres décimaux et unaires simples, je présente les méthodes alternatives suivantes, avec leurs avantages et leurs inconvénients.

Décimal à unaire simple: 102 + 1 (indicateur r) = 103 octets. J'ai compté \tcomme un onglet littéral, comme 1 octet.

h
:
s:\w::2g
y:9876543210:87654321\t :
/ /!s:$:@:
/\s/!t
x;s:-?.::;x
G;s:\s::g
/\w/{s:@:&&&&&&&&&&:g;t}

Essayez-le en ligne!

Avantage: il est 22 octets plus court et en plus, il fonctionne avec des entiers négatifs en entrée

Inconvénient: il écrase l'espace d'attente. Cependant, comme il est plus probable que vous ayez besoin de convertir l'entier d'entrée au début du programme, cette limitation est rarement ressentie.

Ordinaire unaire à décimal: 102 + 1 (indicateur r) = 103 octets

s:-?:&0:
/@/{:
s:\b9+:0&:
s:.9*@:/&:
h;s:.*/::
y:0123456789:1234567890:
x;s:/.*::
G;s:\n::
s:@::
/@/t}

Essayez-le en ligne!

Avantage: il est plus court de 14 octets. Cette fois, les deux versions de pourboire fonctionnent en entrée pour les entiers négatifs.

Inconvénient: il écrase l'espace d'attente

Pour un défi compliqué, vous devrez adapter ces extraits pour qu'ils fonctionnent avec d'autres informations pouvant exister dans l'espace modèle ou l'espace réservé, en plus du nombre à convertir. Le code peut être joué plus, si vous savez que vous ne travaillez qu'avec des nombres positifs ou que le zéro seul ne sera pas une entrée / sortie valide.

Un exemple d'une telle réponse de défi, où j'ai créé et utilisé ces extraits, est l' inverse d'un nombre (1 / x) .


Pour unaire à décimal , vous pouvez enregistrer deux octets en combinant les deux dernières substitutions: s:\n|@$::g. tio.run/##K05N@f@/2ErX3krNwIpL30G/…
Jordanie

J'ai eu mon propre essai du convertisseur décimal en unaire. Voici 97 octets :) Essayez-le en ligne! (ne nécessite pas non plus -r, mais avec un nouveau consensus, les drapeaux ne comptent pas dans le décompte de toute façon, et cela ne gâche pas l'espace d'attente)
Kritixi Lithos

En fait, si vous changez la dernière ligne de /\n/taen /\n/t, vous économisez 1 octet pour obtenir 96
Kritixi Lithos

@Cowsquack Merci, 96 c'est super! Vous n'avez pas le temps maintenant, vous y réfléchirez ce week-end.
seshoumara

Bien sûr, envoyez-moi un ping sur le chat alors :)
Kritixi Lithos

3

Parlons des commandes tet T, bien qu'elles soient expliquées dans la page de manuel, il est facile de les oublier et d'introduire des bogues accidentellement, surtout lorsque le code se complique.

Déclaration de page de manuel pour t:

Si a s///a effectué une substitution réussie depuis la lecture de la dernière ligne d'entrée et depuis la dernière commande t ou T, branchez-vous sur label.

Exemple montrant ce que je veux dire: disons que vous avez une liste de nombres et que vous voulez compter le nombre de négatifs. Code partiel ci-dessous:

1{x;s/.*/0/;x}                   # initialize the counter to 0 in hold space
s/-/&/                           # check if number is negative
t increment_counter              # if so, jump to 'increment_counter' code block
b                                # else, do nothing (start a next cycle)

:increment_counter
#function code here

Ça a l'air bien, mais ça ne l'est pas. Si le premier nombre est positif, ce code pensera toujours qu'il était négatif, car le saut effectué via tpour la première ligne d'entrée est effectué malgré tout, car il y a eu une ssubstitution réussie lorsque nous avons initialisé le compteur! Le correct est:/-/b increment_counter .

Si cela vous paraissait facile, vous pourriez tout de même être dupe lorsque vous effectuez plusieurs sauts d'avant en arrière pour simuler des fonctions. Dans notre exemple, le increment_counterbloc de code utiliserait certainement beaucoup de scommandes. Revenir avec b mainpourrait faire tomber un autre check dans "main" dans le même piège. C'est pourquoi je reviens habituellement des blocs de code avec s/.*/&/;t label. C'est moche, mais utile.


2

Au lieu d'effacer l'espace de motif avec s/.*//, utilisez la zcommande (en minuscules) si vous optez pour GNU sed. Outre le nombre d'octets inférieur, il présente l'avantage de ne pas démarrer le cycle suivant comme le dfait la commande , ce qui peut être utile dans certaines situations.


1
Peut également être utile si vous avez des séquences multi-octets invalides (auxquelles ne correspond pas .).
Toby Speight

2

Je sais que c'est un vieux thread, mais je viens de trouver ces convertisseurs décimaux maladroits en UCD, avec près d'une centaine d'octets, certains gâchant même l'espace d'attente ou nécessitant des défauts particuliers sed versions .

Pour la décimale en UCD, j'utilise (68 octets; l'ancien meilleur affiché ici 87 octets)

s/$/\n9876543210/
:a
s/\([1-9]\)\(.*\n.*\)\1\(.\)/\3x\2\1\3/
ta
P;d

UCD en décimal est (également 66 octets; ancien meilleur affiché ici 96)

s/$/\n0123456789/
:a      
s/\([0-8]\)x\(.*\n.*\)\1\(.\)/\3\2\1\3/
ta      
P;d
  • \ndans le remplacement n'est pas portable. Vous pouvez utiliser un caractère différent à la place et enregistrer deux octets, mais vous aurez besoin de plus d'octets pour supprimer l'appendice au lieu de P;d; voir remarque suivante. Ou, si votre espace d'attente est vide, faitesG;s/$/9876543210/ sans pénalité d'octet.
  • Si vous avez besoin d'un traitement supplémentaire, vous aurez besoin de plus d'octets pour s/\n.*//au lieu deP;d .
  • Vous pouvez enregistrer deux octets chacun pour ces vieux GNU buggés sed versions de
  • Non, vous ne pouvez pas enregistrer ces six barres obliques inverses car les expressions régulières étendues ne font pas de rétro-références

Il n'y a pas de convertisseurs décimaux vers UCD et antérieurs publiés dans ce fil qui gâchent l'espace d'attente ou nécessitent des versions sed défectueuses.
seshoumara

Votre propre réponse du 6 avril utilise l'espace or et ne fonctionnera qu'avec les anciennes sedversions qui violent la norme POSIX.
Philippos

Je ne fais pas de conversions décimales en UCD! Lisez attentivement le fil. UCD signifie que 12 est converti en 0x0xx (ce que votre réponse calcule), tandis que tout simplement unaire (ce que ma réponse calcule) signifie que 12 est converti en xxxxxxxxxxxx. J'ai choisi @ comme symbole, mais vous voyez l'idée. Et en outre, sur PPCG, il n'est pas nécessaire d'adhérer au standard POSIX.
seshoumara

Si cela vous plaît, shérif
Philippos

2

Lisez l'intégralité de l'entrée en même temps avec -z

Souvent, vous devez opérer sur l'ensemble de l'entrée à la fois au lieu d'une ligne à la fois. La Ncommande est utile pour cela:

:
$!{N;b}

... mais généralement vous pouvez l'ignorer et utiliser le -zdrapeau à la place.

Le -zdrapeau fait que sed utilise NUL ( \0) comme séparateur de ligne d'entrée au lieu de \n, donc si vous savez que votre entrée ne contiendra pas \0, il lira toutes les entrées à la fois comme une seule "ligne":

$ echo 'foo
> bar
> baz' | sed -z '1y/ao/eu/'
fuu
ber
bez

Essayez-le en ligne!


2

Ajouter une nouvelle ligne dans un octet

La Gcommande ajoute une nouvelle ligne et le contenu de l'espace d'attente à l'espace modèle, donc si votre espace d'attente est vide, au lieu de cela:

s/$/\n/

Tu peux le faire:

G

Ajouter une nouvelle ligne en trois octets

La Hcommande ajoute une nouvelle ligne et le contenu de l'espace de motif à l'espace d'attente, et xéchange les deux, donc si votre espace d'attente est vide, au lieu de cela:

s/^/\n/

Tu peux le faire:

H;x

Cela polluera votre espace de retenue, donc cela ne fonctionne qu'une seule fois. Cependant, pour deux octets supplémentaires, vous pouvez effacer votre espace de modèle avant de permuter, ce qui représente toujours une économie de deux octets:

H;z;x

1

Dans sed, la chose la plus proche d'une fonction que vous pouvez avoir est une étiquette. Une fonction est utile car vous pouvez exécuter son code plusieurs fois, économisant ainsi beaucoup d'octets. Dans sed, cependant, vous devrez spécifier l'étiquette de retour et en tant que tel, vous ne pouvez pas simplement appeler cette "fonction" plusieurs fois dans votre code comme vous le feriez dans d'autres langues.

La solution de contournement que j'utilise consiste à ajouter dans l'une des deux mémoires un indicateur, qui est utilisé pour sélectionner l'étiquette de retour. Cela fonctionne mieux lorsque le code de fonction n'a besoin que d'un seul espace mémoire (l'autre).

Exemple montrant ce que je veux dire: extrait d'un de mes projets pour écrire un petit jeu dans sed

# after applying the player's move, I overwrite the pattern space with the flag "P"
s/.*/P/
b check_game_status
:continue_turn_from_player
#code

b calculate_bot_move
:return_bot_move
# here I call the same function 'check_game_status', but with a different flag: "B"
s/.*/B/
b check_game_status
:continue_turn_from_bot
#code (like say 'b update_screen')

:check_game_status   # this needs just the hold space to run
#code
/^P$/b continue_turn_from_player
/^B$/b continue_turn_from_bot

Les étiquettes doivent bien sûr être regroupées en une seule lettre, j'ai utilisé des noms complets pour une meilleure explication.


1

Les expressions rationnelles vides sont équivalentes à l'expression régulière rencontrée précédemment

(merci à Riley de l' avoir découvert à partir d'une soumission anagol )

Voici un exemple où nous sommes chargés de créer 100 @s dans un tampon vide.

s/$/@@@@@@@@@@/;s/.*/&&&&&&&&&&/ # 31 bytes
s/.*/@@@@@@@@@@/;s//&&&&&&&&&&/  # 30 bytes

La deuxième solution est plus courte d'un octet et utilise le fait que les expressions régulières vides sont remplies avec la dernière expression régulière rencontrée. Ici, pour la deuxième substitution, le dernier regex était .*, donc le regex vide ici sera rempli .*. Cela fonctionne également avec les expressions rationnelles dans/conditionals/ .

Notez qu'il s'agit de l' expression régulière rencontrée précédemment , donc les éléments suivants fonctionneraient également.

s/.*/@@@@@@@@@@/;/@*/!s/$/@/;s//&&&&&&&&&&/

La regex vide est remplie au @*lieu de $car s/$/@/n'est jamais atteinte.


Oui, bonne réponse. J'ai même allongé les regex afin qu'elles puissent être recomposées comme ceci (raccourcissant ainsi le programme).
Toby Speight

0

Étape presque inutile:

y|A-y|B-z|

Cela ne traduit Aà Bet yà z(... et -à -;), mais rien d' autre, si

sed -e 'y|A-y|B-z|' <<<'Hello world!'

reviendra simplement:

Hello world!

Vous pouvez vous assurer que ce sera inutile, pour l' échantillon en utilisant cette sur des valeurs hexadécimaux minuscules (ne contenant que 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, eou f.)


2
Est-ce quelque chose que vous avez découvert à la dure?! ;-)
Toby Speight

J'aime les scripts inutiles: sed '; ;/s/b;y|A-y|B-z|;s ;s/ //; ; ;' <<<'Hello world'(Pourquoi cela ne supprime -t-il pas l'espace?)
F. Hauri
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.