Suggerimenti per giocare a golf a sed


19

Quali consigli generali hai per giocare a golf a sed? Sto cercando idee che possano essere applicate a problemi di code-golf e che siano anche in qualche modo specifiche per sed (es. "Rimuovere commenti" non è una risposta).

Si prega di inviare un suggerimento per risposta.


4
In realtà non è un consiglio per il golf (ma comunque un consiglio per il golf): i feed di linea consumano tanti byte quanti punti e virgola, quindi puoi mantenere il tuo codice breve e leggibile.
Dennis,

Nemmeno un suggerimento, ma un problema: ho GNU sed, eppure il Fcomando non ha mai funzionato. Qualcuno sa perché?
seshoumara,

@seshoumara Ffunziona sul mio GNU sed (test Debian). Stampa solo -se si legge dallo stdin, ovviamente, ma è previsto. Da cosa ottieni sed -e 'F;Q' /etc/hostname?
Toby Speight,

@TobySpeight Che dà questo errore: char 1: unknown command: F. Devo aggiornare sed forse; Quale versione hai? Anche il Lcomando non funziona, ma è comunque inutile poiché -l nesiste. Tutto il resto menzionato sul sito di GNU sed funziona.
seshoumara,

1
Ho aperto la chat room bash, sed and dcper tutti coloro che vogliono parlare e chiedere di queste lingue. Facciamo una comunità!
seshoumara,

Risposte:


11

Se devi utilizzare le etichette, sicuramente vorrai che i nomi delle tue etichette siano i più corti possibile. In effetti portato all'estremo, puoi persino usare la stringa vuota come nome di un'etichetta:

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


In effetti, ecco anche l'effettivo link git commit . Immagino che per PPCG questo non cambierà molto, dal momento che ci è consentito pubblicare risposte per GNU sed 4.2.x, ma è bene sapere, anche se purtroppo, che questo trucco non funzionerà più ufficialmente.
seshoumara,

8

La documentazione GNU sed descrive il scomando come "coltellino svizzero di sed" . Ma se tutto ciò che vuoi fare è sostituire tutte le istanze di un personaggio con un altro, allora il ycomando è quello che ti serve:

y/a/b/

è un carattere più corto di:

s/a/b/g

è anche molto più veloce e può scambiare i caratteri sul posto:y/12/21/
mikeserv il

6

Prendi in considerazione l'utilizzo della sintassi regex estesa (in GNU sed). L' -ropzione costa un byte nel calcolo del punteggio, ma usarla solo una volta per eliminare le barre rovesciate da una coppia di \(...\)ha già pagato per sé.


2
Con la nota aggiuntiva che -rsembra essere sedspecifica per GNU .
arte

@manat - aggiunto (ma è una risposta Wiki della community, quindi avresti potuto modificare te stesso).
Toby Speight,

Ovviamente. Non l'ho considerato parte del suggerimento, solo una nota aggiuntiva.
arte

E si continua a pagare per se stessa quando si usano +, ?, {}e |nelle partite regex, in quanto non sono necessari backslash sia.
seshoumara,

-Efunziona come alias -rin molte sedimplementazioni se ricordo bene.
phk,

6

Quando si sostituisce ripetutamente in un ciclo:

loop:
s/foo/bar/g
tloop

di solito non è necessario sostituire globalmente, poiché il ciclo alla fine sostituirà tutte le occorrenze:

# GNU sed
:
s/foo/bar/
t

Nota anche l'estensione GNU sopra: un'etichetta può avere un nome vuoto, salvando byte più preziosi. In altre implementazioni, un'etichetta non può essere vuota e saltare senza un'etichetta trasferisce il flusso alla fine dello script (ovvero lo stesso di n).


1
Il nome dell'etichetta vuota è specifico per GNU, POSIX richiede rami senza argomenti per saltare alla fine dello script (sembra essere il comportamento in BSD e Busybox, anche in GNU sed se non aggiungi un vuoto :)
ninjalj

2
L'etichetta senza nome era sempre un bug in GNU sed, non un'estensione, e nella versione 4.3 e successive questo bug era, purtroppo, corretto. Vedi qui .
seshoumara,

5

Non esiste un'aritmetica incorporata, ma i calcoli possono essere eseguiti in formato decimale unario o in codice unario. Il seguente codice converte il decimale in UCD, con x come unità e 0 come separatore delle cifre:

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

ed ecco la conversione di nuovo in decimale:

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

Entrambi sono presi da una risposta a "Moltiplica due numeri senza usare alcun numero" .

Unary vecchio semplice può essere convertito usando questa coppia di loop da questa risposta in "{Curly Numbers};" , dove si trova l'unità ;. Ho usato ve xabbinare Roman per 5e 10; bviene da "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
... e se devi usare uno di questi, quasi sicuramente hai già perso il codice golf, anche se potresti essere ancora competitivo con le risposte di Java ;-) Tuttavia, è comunque divertente da usare.
Digital Trauma,

La conversione da unario semplice a decimale fornisce risposte errate per l'equivalente input unario della forma decimale X0X, ad esempio 108. La linea responsabile di ciò è /[;v]/!s/\b/0/2, che deve essere cambiata /[;v]/!s:x\+:&0:per farla funzionare. Vedi qui .
seshoumara,

@seshoumara, il tuo link sembra essere una pagina vuota. Ma è del tutto plausibile che ho fatto un errore durante l'estrazione di quel codice dalla risposta di riferimento, quindi applicherò solo la tua correzione.
Toby Speight,

Il collegamento si carica correttamente, ma mi aspettavo qualcosa di diverso da una pagina grigia con "TIO" e qualcosa che assomiglia al logo di Ubuntu - è questo che è previsto? E mi riferivo alla seconda delle risposte a cui ho fatto riferimento ( 58007 ), poiché è qui che ha avuto origine il campione semplice-unario.
Toby Speight,

Il collegamento TIO avrebbe dovuto contenere il codice corretto, oltre a un input di esempio, 108 in unario. Sull'esecuzione del codice dovresti aver visto il risultato corretto 108 e non 180, come precedentemente generato da quella riga di codice ora fissa. L'aggiornamento della risposta referenziata dipende interamente da te. Questa è una wiki della comunità.
seshoumara,

4

Come menzionato in man sed(GNU), puoi usare qualsiasi carattere come delimitatore per le espressioni regolari usando la sintassi

\%regexp%

dove %è un segnaposto per qualsiasi personaggio.

Questo è utile per comandi come

/^http:\/\//

che sono più brevi come

\%^http://%

Ciò che è menzionato nel manuale di GNU sed, ma non in, man sedè che puoi modificare anche i delimitatori di s///e y///.

Ad esempio, il comando

ss/ssg

rimuove tutte le barre dallo spazio del motivo.


4

Se non esplicitamente vietato dalla domanda, il consenso per questa meta domanda è che l'input numerico può essere unario. Ciò consente di risparmiare 86 byte di decimale in unario secondo questa risposta .


Questo meta consenso non è per sed riferito al semplice vecchio formato unario? Ho diverse risposte in cui un input in UCD mi aiuterebbe, nel caso in entrambi i casi.
seshoumara,

@seshoumara intendevo unario, non UCD
Digital Trauma,

Quindi la conversione da decimale a semplice vecchio unario ti fa risparmiare 126 byte secondo la risposta che hai collegato. 86 byte è per la conversione in UCD.
seshoumara,

4

Espandendo su questa risposta di suggerimento , per quanto riguarda le conversioni tra i formati numerici unari decimali e semplici, presento i seguenti metodi alternativi, con i loro vantaggi e svantaggi.

Da decimale a semplice unario: 102 + 1 (flag r) = 103 byte. Ho contato \tcome una scheda letterale, come 1 byte.

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

Provalo online!

Vantaggio: è più breve di 22 byte e, come extra, funziona con numeri negativi come input

Svantaggio: sovrascrive lo spazio di attesa. Tuttavia, poiché è più probabile che tu abbia bisogno di convertire l'intero di input proprio all'inizio del programma, questa limitazione si sente raramente.

Semplice da unario a decimale: 102 + 1 (flag r) = 103 byte

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

Provalo online!

Vantaggio: è più breve di 14 byte. Questa volta entrambe le versioni di punta funzionano come numeri interi negativi come input.

Svantaggio: sovrascrive lo spazio di attesa

Per una sfida complicata, dovrai adattare questi frammenti per lavorare con altre informazioni che possono esistere nello spazio del modello o contenere spazio, oltre al numero da convertire. Il codice può essere golfato di più, se sai che lavori solo con numeri positivi o che lo zero da solo non sarà un input / output valido.

Un esempio di tale risposta alla sfida, in cui ho creato e utilizzato questi frammenti, è il reciproco di un numero (1 / x) .


Per unario a decimale è possibile salvare due byte combinando le ultime due sostituzioni: s:\n|@$::g. tio.run/##K05N@f@/2ErX3krNwIpL30G/…
Giordania

Ho provato il convertitore da decimale a unario. Ecco 97 byte :) Provalo online! (inoltre non richiede -r, ma con un nuovo consenso, le bandiere non contano comunque per il bytecount e non
rovinano lo

In realtà se cambi l'ultima riga da /\n/taa /\n/t, risparmi 1 byte per ottenere 96
Kritixi Lithos

@Cowsquack Grazie, 96 è fantastico! Non ho tempo ora, lo guarderò questo fine settimana.
seshoumara,

Certo, mandami un ping in chat allora :)
Kritixi Lithos,

3

Parliamo dei comandi te T, che sebbene siano spiegati nella pagina man, è facile dimenticarsene e introdurre errori accidentalmente, specialmente quando il codice diventa complicato.

Dichiarazione della pagina man per t:

Se a s///ha eseguito correttamente la sostituzione dall'ultima riga di input e dall'ultimo comando t o T, quindi passare all'etichetta.

Esempio che mostra cosa intendo: supponiamo che tu abbia un elenco di numeri e desideri contare quanti negativi ci sono. Codice parziale di seguito:

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

Sembra ok, ma non lo è. Se il primo numero è positivo, quel codice penserà comunque che fosse negativo, perché il salto effettuato tramite tper la prima riga di input viene eseguito indipendentemente, poiché si è verificata una ssostituzione riuscita quando abbiamo inizializzato il contatore! Corretta è: /-/b increment_counter.

Se questo sembra facile, potresti essere ancora preso in giro quando fai più salti avanti e indietro per simulare le funzioni. Nel nostro esempio il increment_counterblocco di codice userebbe sicuramente molti scomandi. Il ritorno con b mainpotrebbe causare un altro check in "main" nella stessa trappola. Ecco perché di solito ritorno dai blocchi di codice con s/.*/&/;t label. È brutto, ma utile.


2

Invece di cancellare lo spazio del pattern con s/.*//, usa il zcomando (minuscolo) se vai con GNU sed. Oltre al conteggio dei byte inferiori, ha il vantaggio di non avviare il ciclo successivo come fa il comando d, il che può essere utile in determinate situazioni.


1
Può anche essere utile se si dispone di sequenze multibyte non valide (che non corrispondono .).
Toby Speight,

2

So che questo è un vecchio thread, ma ho appena trovato quei goffi decimali in convertitori UCD, con quasi un centinaio di byte, alcuni addirittura rovinando lo spazio di attesa o richiedendo speciali sedversioni difettose .

Per i decimali in UCD che uso (68 byte; precedentemente meglio pubblicato qui 87 byte)

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

UCD in decimale è (anche 66 byte; ex meglio pubblicato qui 96)

s/$/\n0123456789/
:a      
s/\([0-8]\)x\(.*\n.*\)\1\(.\)/\3\2\1\3/
ta      
P;d
  • \nnella sostituzione non è portatile. Puoi invece usare un carattere diverso e salvare due byte, ma avrai bisogno di più byte per rimuovere l'appendice invece di P;d; vedi la prossima osservazione. Oppure, se il tuo spazio di attesa è vuoto, fai a G;s/$/9876543210/meno della penalità di byte.
  • Se hai bisogno di ulteriore elaborazione, avrai bisogno di qualche byte in più s/\n.*//invece di P;d.
  • È possibile salvare due byte ciascuno per quelle vecchie sedversioni GNU difettose
  • No, non è possibile salvare queste sei barre rovesciate poiché le espressioni regolari estese non fanno riferimenti

Non ci sono decimali in UCD e convertitori posteriori pubblicati in questo thread che rovinano lo spazio di attesa o richiedono versioni sed difettose.
seshoumara,

La tua risposta dal 6 aprile utilizza lo spazio d'oro e funzionerà solo con le vecchie sedversioni che violano lo standard POSIX.
Filippos,

Non sto facendo conversioni decimali in UCD! Leggi di nuovo il filo con attenzione. UCD significa che 12 viene convertito in 0x0xx (ciò che calcola la tua risposta), mentre in chiaro unario (ciò che calcola la mia risposta) significa che 12 viene convertito in xxxxxxxxxxxx. Ho scelto @ come simbolo, ma hai avuto l'idea. Inoltre, su PPCG non è necessario aderire allo standard POSIX.
seshoumara,

Se ti fa piacere, sceriffo
Filippos,

2

Leggi l'intero input contemporaneamente con -z

Spesso è necessario operare sull'intero input contemporaneamente anziché su una riga alla volta. Il Ncomando è utile per questo:

:
$!{N;b}

... ma di solito puoi saltarlo e usare -zinvece la bandiera.

Il -zflag fa sì che sed usi NUL ( \0) come suo separatore di riga di input invece di \n, quindi se sai che il tuo input non conterrà \0, leggerà tutto l'input in una sola volta come una singola "linea":

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

Provalo online!


2

Aggiungi una nuova riga in un byte

Il Gcomando aggiunge una nuova riga e il contenuto dello spazio di attesa allo spazio del motivo, quindi se lo spazio di attesa è vuoto, invece di questo:

s/$/\n/

Puoi farlo:

G

Prepara una nuova riga in tre byte

Il Hcomando aggiunge una nuova riga e il contenuto dello spazio del motivo allo spazio di attesa e xscambia i due, quindi se lo spazio di attesa è vuoto, invece di questo:

s/^/\n/

Puoi farlo:

H;x

Questo inquinerà il tuo spazio di stiva, quindi funziona solo una volta. Per altri due byte, tuttavia, è possibile cancellare lo spazio del modello prima dello scambio, il che comporta comunque un risparmio di due byte:

H;z;x

1

In sed, la cosa più vicina a una funzione che puoi avere è un'etichetta. Una funzione è utile perché è possibile eseguire il suo codice più volte, risparmiando così molti byte. In sed, tuttavia, è necessario specificare l'etichetta di restituzione e come tale non è possibile semplicemente chiamare questa "funzione" più volte nel codice come si farebbe in altre lingue.

La soluzione alternativa che uso è quella di aggiungere in una delle due memorie un flag, che viene utilizzato per selezionare l'etichetta di ritorno. Funziona meglio quando il codice funzione necessita solo di un singolo spazio di memoria (l'altro).

Esempio che mostra cosa intendo: tratto da un mio progetto per scrivere un piccolo gioco in 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

Le etichette dovrebbero ovviamente essere giocate a golf con una sola lettera, ho usato i nomi completi per una spiegazione migliore.


1

Le regex vuote equivalgono alla regex precedentemente incontrata

(grazie a Riley per averlo scoperto da una presentazione anagol )

Ecco un esempio in cui ci viene assegnato il compito di creare 100 @s in un buffer vuoto.

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

La seconda soluzione è più corta di 1 byte e utilizza il fatto che le regex vuote vengono riempite con l'ultima regex riscontrata. Qui, per la seconda sostituzione, è stata l'ultima regex .*, quindi la regex vuota verrà riempita .*. Questo funziona anche con regexes in /conditionals/.

Si noti che è la regex precedentemente incontrata , quindi anche quanto segue funzionerebbe.

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

La regex vuota viene riempita @*anziché $perché s/$/@/non viene mai raggiunta.


Sì, buona risposta Ho anche allungato le regex più a lungo in modo che possano essere abbinate di nuovo in questo modo (rendendo il programma più breve).
Toby Speight,

0

Passo per lo più inutile:

y|A-y|B-z|

Questo si tradurrà solo Ain Be yin z(... e -in -;), ma nient'altro, quindi

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

tornerà solo:

Hello world!

Si potrebbe assicurare questo sarà inutile, per esempio utilizzando questo su valori esadecimali minuscole (contenente solo 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, eo f.)


2
È qualcosa che hai scoperto nel modo più duro ?! ;-)
Toby Speight,

Mi piacciono gli script inutili: sed '; ;/s/b;y|A-y|B-z|;s ;s/ //; ; ;' <<<'Hello world'(Perché questo non sopprime lo spazio?)
F. Hauri,
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.