Wskazówki dotyczące gry w golfa w Sed

19

Jakie masz ogólne wskazówki na temat gry w golfa w sed? Szukam pomysłów, które można by zastosować do problemów związanych z golfem i które są przynajmniej nieco specyficzne dla sed (np. „Usuń komentarze” nie jest odpowiedzią).

Proszę zamieścić jedną wskazówkę na odpowiedź.

Toby Speight
źródło
4
Nie jest to tak naprawdę wskazówka do gry w golfa (ale wciąż wskazówka do gry w golfa): źródła linii zużywają tyle samo bajtów, co średników, dzięki czemu kod może być krótki i czytelny.
Dennis
Ani wskazówka, ale problem: mam GNU sed, ale Fpolecenie nigdy nie działało. Czy ktoś wie dlaczego?
seshoumara,
@seshoumara Fdziała na moim GNU sed (testowanie Debiana). Oczywiście drukuje się -tylko przy czytaniu ze standardowego wejścia, ale jest to oczekiwane. Co otrzymasz od sed -e 'F;Q' /etc/hostname?
Toby Speight
@TobySpeight To daje ten błąd: char 1: unknown command: F. Może muszę zaktualizować sed; jaką masz wersję? LPolecenie również nie działa, ale to nie ma sensu, ponieważ w każdym razie -l nistnieje. Wszystko inne wymienione na stronie GNU sed działa.
seshoumara
1
Otworzyłem pokój czatu bash, sed and dcdla wszystkich, którzy chcą rozmawiać i pytać o te języki. Stwórzmy społeczność!
seshoumara,

Odpowiedzi:

11

Jeśli potrzebujesz użyć etykiet, na pewno będziesz chciał, aby nazwy etykiet były jak najkrótsze. W rzeczywistości doprowadzono do skrajności, możesz nawet użyć pustego ciągu jako nazwy etykiety:

:    # define label ""
p    # print pattern space
b    # infinite loop! - branch to label ""
Cyfrowa trauma
źródło
Rzeczywiście, tutaj jest też link do git commit . Sądzę, że w przypadku PPCG niewiele się to zmieni, ponieważ możemy publikować odpowiedzi dla GNU sed 4.2.x, ale dobrze jest wiedzieć, choć niestety, że ta sztuczka już oficjalnie nie zadziała.
seshoumara
8

Dokumentacja GNU sed opisuje spolecenie jako „szwajcarski scyzoryk sed” . Ale jeśli wszystko, co chcesz zrobić, to zastąpić wszystkie wystąpienia jednej postaci inną, wtedy ypolecenie jest tym, czego potrzebujesz:

y/a/b/

jest o jeden char krótszy niż:

s/a/b/g
Cyfrowa trauma
źródło
jest również znacznie szybszy i może zamieniać znaki w miejscu:y/12/21/
mikeserv
6

Rozważ użycie rozszerzonej składni wyrażenia regularnego (w GNU sed). -rOpcja kosztuje jeden bajt w punktacji, ale używając go tylko raz, aby wyeliminować backslashy z parą \(...\)już zapłacił za siebie.

Toby Speight
źródło
2
Z dodatkową uwagą, która -rwydaje się być sedspecyficzna dla GNU .
manatwork
@manat - dodano (ale jest to odpowiedź Wiki Wiki, więc mógłbyś dokonać edycji).
Toby Speight,
Oczywiście. Po prostu nie uważałem tego za wskazówkę, tylko dodatkową notatkę.
manatwork
I to wciąż płaci za siebie podczas używania +, ?, {}a |w regex dopasowania, ponieważ nie są potrzebne ani ukośniki.
seshoumara,
-Edziała jako alias do -rwielu sedimplementacji, jeśli dobrze pamiętam.
phk
6

Podczas wielokrotnego zastępowania w pętli:

loop:
s/foo/bar/g
tloop

Zazwyczaj globalne nie jest konieczne, ponieważ pętla ostatecznie zastąpi wszystkie wystąpienia:

# GNU sed
:
s/foo/bar/
t

Zwróć także uwagę na powyższe rozszerzenie GNU: etykieta może mieć pustą nazwę, oszczędzając cenniejsze bajty. W innych implementacjach etykieta nie może być pusta, a przeskakiwanie bez przeniesienia etykiety przepływa do końca skryptu (tj. Takiego samego jak n).

Toby Speight
źródło
1
Nazwa pustej etykiety jest specyficzna dla GNU, POSIX wymaga gałęzi bez argumentu, aby przejść do końca skryptu (wydaje się być zachowaniem w BSD i Busybox, także w GNU sed, jeśli nie dodasz pustego :)
ninjalj
2
Bezimienna etykieta zawsze była błędem w GNU sed, a nie rozszerzeniem, aw wersji 4.3 i wyższej błąd ten został niestety naprawiony. Zobacz tutaj .
seshoumara
5

Nie ma wbudowanej arytmetyki, ale obliczenia można wykonywać w jednostajnym lub jednokodowanym dziesiętnym. Poniższy kod konwertuje liczbę dziesiętną na UCD, gdzie x to jednostka, a 0 jako separator cyfr:

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

a oto konwersja z powrotem na dziesiętne:

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

Oba są zaczerpnięte z odpowiedzi na „Pomnóż dwie liczby bez użycia żadnych liczb” .

Zwykłe stare jednoargumentowe można przekonwertować za pomocą tej pary pętli z tej odpowiedzi na „{Curly Numbers};” , gdzie jest jednostka ;. Użyłem vi xdopasowałem Roman do 5i 10; bpochodzi od „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
Toby Speight
źródło
1
... a jeśli musisz użyć któregoś z nich, prawie na pewno straciłeś już kod golfa, choć nadal możesz konkurować z odpowiedziami w języku Java ;-) Mimo to przyjemnie się z niego korzysta.
Cyfrowa trauma
Konwersja zwykłego jednorzędowego na dziesiętny daje błędne odpowiedzi dla jednoargumentowego odpowiednika postaci dziesiętnej X0X, na przykład 108. Odpowiada za to wiersz /[;v]/!s/\b/0/2, który należy zmienić /[;v]/!s:x\+:&0:, aby działał. Zobacz tutaj .
seshoumara,
@seshoumara, twój link wydaje się być pustą stroną. Ale jest całkowicie prawdopodobne, że popełniłem błąd podczas wyodrębniania tego kodu z odpowiedzi, do której się odwołuje, więc po prostu zastosuję poprawkę.
Toby Speight
Link ładuje się poprawnie, ale spodziewałem się czegoś innego niż szara strona z „TIO” i czymś, co wygląda jak logo Ubuntu - czy to jest to, co jest zamierzone? Odniosłem się do drugiej odpowiedzi, do której się odniosłem ( 58007 ), ponieważ stąd pochodzi zwykła próbka.
Toby Speight
Łącze TIO powinno zawierać poprawiony kod, a także przykładowe dane wejściowe 108 w unary. Po uruchomieniu kodu powinieneś zobaczyć poprawny wynik 108, a nie 180, jak poprzednio wygenerowany przez ten teraz naprawiony wiersz kodu. Aktualizacja odpowiedzi, do której się odwołujesz, zależy wyłącznie od Ciebie. To jest wiki społeczności.
seshoumara,
4

Jak wspomniano w man sed(GNU), możesz użyć dowolnego znaku jako separatora dla wyrażeń regularnych, używając składni

\%regexp%

gdzie %jest symbolem zastępczym dla dowolnej postaci.

Jest to przydatne dla poleceń takich jak

/^http:\/\//

które są krótsze jako

\%^http://%

W podręczniku GNU sed wspomniano, ale nie w man sedtym, że można zmienić ograniczniki s///i y///.

Na przykład polecenie

ss/ssg

usuwa wszystkie ukośniki z obszaru wzoru.

Dennis
źródło
4

Jeśli pytanie nie wyraźnie tego zabrania, konsensus w przypadku tego meta pytania jest taki, że dane liczbowe mogą być jednomyślne . To oszczędza ci 86 bajtów dziesiętnych do jednych zgodnie z tą odpowiedzią .

Cyfrowa trauma
źródło
Czy to nie jest meta konsensus dla sed odnoszący się do zwykłego starego, jednolitego formatu? Mam kilka odpowiedzi, w których wejście w UCD mogłoby mi pomóc, na wypadek, gdyby tak było.
seshoumara,
@seshoumara Miałem na myśli unary, a nie UCD
Digital Trauma
Następnie konwersja z dziesiętnej na zwykłą starą unarną oszczędza Ci 126 bajtów zgodnie z tą odpowiedzią, którą podłączyłeś. 86 bajtów służy do konwersji na UCD.
seshoumara
4

Rozwijając tę odpowiedź , dotyczącą konwersji między formatami liczb dziesiętnych i zwykłych liczb jednoznacznych, przedstawiam następujące alternatywne metody z ich zaletami i wadami.

Liczba dziesiętna do zwykłej: 102 + 1 (flaga r) = 103 bajty. Liczę \tjako dosłowną kartę, jako 1 bajt.

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

Wypróbuj online!

Zaleta: jest o 22 bajty krótszy, a dodatkowo działa z ujemnymi liczbami całkowitymi jako danymi wejściowymi

Wada: zastępuje przestrzeń wstrzymania. Ponieważ jednak bardziej prawdopodobne jest, że będziesz musiał przekonwertować całkowitą liczbę wejściową na samym początku programu, ograniczenie to jest rzadko odczuwalne.

Zwykły od jednego do dziesiętnego: 102 + 1 (flaga r) = 103 bajty

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

Wypróbuj online!

Zaleta: jest 14 bajtów krótszy. Tym razem obie wersje końcówek działają jako ujemne liczby całkowite jako dane wejściowe.

Wada: zastępuje przestrzeń wstrzymania

W przypadku skomplikowanego wyzwania musisz dostosować te fragmenty, aby współpracowały z innymi informacjami, które mogą istnieć w obszarze wzorca lub w miejscu przechowywania oprócz liczby do konwersji. Kod może być bardziej golfowany, jeśli wiesz, że pracujesz tylko z dodatnimi liczbami lub że samo zero nie będzie prawidłowym wejściem / wyjściem.

Przykładem takiej odpowiedzi na wyzwanie, w której stworzyłem i wykorzystałem te fragmenty, jest odwrotność liczby (1 / x) .

seshoumara
źródło
Dla jednoskładnikowa do przecinku można zapisać dwa bajty, łącząc dwie ostatnie podstawienia: s:\n|@$::g. tio.run
Jordan
Miałem własną próbę przeliczenia dziesiętnego na jednostkowy. Oto 97 bajtów :) Wypróbuj online! (również nie wymaga -r, ale przy nowym konsensusie flagi i tak nie liczą się do liczby bajtów , i nie
psuje
W rzeczywistości, jeśli zmienisz ostatnią linię z /\n/tana /\n/t, zaoszczędzisz 1 bajt, aby uzyskać 96
Kritixi Lithos
@ Cowsquack Dzięki, 96 jest świetna! Nie mam teraz czasu, popatrzę na to w ten weekend.
seshoumara
Oczywiście, wyślij mi ping na czacie wtedy :)
Kritixi Lithos
3

Porozmawiajmy o komendach ti T, że chociaż są one wyjaśnione na stronie podręcznika, łatwo o tym zapomnieć i przypadkowo wprowadzić błędy, szczególnie gdy kod się komplikuje.

Instrukcja strony podręcznika dla t:

Jeśli a s///dokonał pomyślnego podstawienia od czasu ostatniego odczytu linii wejściowej i od ostatniego polecenia t lub T, należy przejść do etykiety.

Przykład pokazujący, co mam na myśli: Załóżmy, że masz listę liczb i chcesz policzyć, ile jest negatywów. Kod częściowy poniżej:

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

Wygląda dobrze, ale tak nie jest. Jeśli pierwsza liczba jest dodatnia, kod nadal będzie myślał, że jest ujemna, ponieważ skok wykonany przez tpierwszy wiersz danych wejściowych jest wykonywany niezależnie, ponieważ spodczas inicjalizacji licznika wystąpiło pomyślne podstawienie! Prawidłowe jest: /-/b increment_counter.

Jeśli wydawało się to łatwe, nadal można Cię oszukać, wykonując wiele skoków tam i z powrotem w celu symulacji funkcji. W naszym przykładzie increment_counterblok kodu z pewnością użyłby wielu spoleceń. Powrót z b mainmoże spowodować, że kolejne sprawdzenie w „main” wpadnie w tę samą pułapkę. Dlatego zwykle wracam z bloków kodu za pomocą s/.*/&/;t label. To brzydkie, ale przydatne.

seshoumara
źródło
2

Zamiast wyczyścić przestrzeń wzorców za pomocą s/.*//, użyj zpolecenia (małe litery), jeśli korzystasz z GNU sed. Oprócz niższej liczby bajtów ma tę zaletę, że nie uruchamia następnego cyklu tak, jak drobi to polecenie , co może być przydatne w niektórych sytuacjach.

seshoumara
źródło
1
Może być również korzystne, jeśli masz nieprawidłowe sekwencje wielobajtowe (które nie są dopasowane .).
Toby Speight
2

Wiem, że to stary wątek, ale właśnie znalazłem te niezdarne konwertery dziesiętne na UCD, z prawie stoma bajtami, niektóre nawet bałaganią przestrzeń wstrzymania lub wymagają specjalnych wadliwych sedwersji.

Do dziesiętnej UCD używam (68 bajtów, były najlepiej napisali tutaj 87 bajtów)

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

UCD na dziesiętne to (również 66 bajtów; poprzednio najlepiej opublikowany tutaj 96)

s/$/\n0123456789/
:a      
s/\([0-8]\)x\(.*\n.*\)\1\(.\)/\3\2\1\3/
ta      
P;d
  • \nw zastępstwie nie jest przenośny. Zamiast tego możesz użyć innego znaku i zapisać dwa bajty, ale będziesz potrzebować więcej bajtów, aby usunąć dodatek zamiast P;d; patrz następna uwaga. Lub, jeśli twoje pole trzymania jest puste, nie rób G;s/$/9876543210/kary bez bajtów.
  • Jeśli potrzebujesz dalszego przetwarzania, będziesz potrzebować więcej bajtów s/\n.*//zamiast P;d.
  • Możesz zapisać dwa bajty dla tych błędnych starych sedwersji GNU
  • Nie, nie można zapisać tych sześciu ukośników odwrotnych, ponieważ rozszerzone wyrażenia regularne nie powodują odwołań wstecznych
Philippos
źródło
W tym wątku nie ma konwerterów dziesiętnych na UCD i tylnych, które mogłyby zepsuć przestrzeń wstrzymania lub wymagają wadliwych wersji sed.
seshoumara,
Twoja własna odpowiedź z 6 kwietnia wykorzystuje złotą przestrzeń i będzie działać tylko ze starymi sedwersjami, które naruszają standard POSIX.
Philippos,
Nie wykonuję konwersji dziesiętnej na UCD! Przeczytaj uważnie ponownie wątek. UCD oznacza, że ​​12 jest konwertowane na 0x0xx (co oblicza twoja odpowiedź), podczas gdy zwykły jednoargumentowy (co oblicza moja odpowiedź) oznacza, że ​​12 jest konwertowany na xxxxxxxxxxxx. Wybrałem @ jako symbol, ale masz pomysł. Co więcej, w PPCG nie trzeba przestrzegać standardu POSIX.
seshoumara,
Jeśli ci się podoba, szeryfie
Philippos
2

Przeczytaj cały tekst naraz za pomocą -z

Często trzeba operować całym wejściem zamiast jednego wiersza na raz. NKomenda jest przydatna, że:

:
$!{N;b}

... ale zwykle możesz go pominąć i -zzamiast tego użyć flagi.

-zFlag sprawia sed użytkowania NUL ( \0) jako separator linii wejściowej zamiast \n, więc jeśli znasz swoje wejście nie będzie zawierać \0będzie czytać wszystkie wejścia jednocześnie jako pojedynczy „line”:

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

Wypróbuj online!

Jordania
źródło
2

Dodaj nowy wiersz w jednym bajcie

GPolecenie dołącza do nowej linii i zawartość miejsca przechowywania do przestrzeni wzoru, więc jeśli przestrzeń hold jest pusty, zamiast tego:

s/$/\n/

Możesz to zrobić:

G

Przygotuj nowy wiersz w trzech bajtach

HPolecenie dołącza do nowej linii i zawartości przestrzeni wzorca do miejsca przechowywania i xzamienia dwa, więc jeśli przestrzeń hold jest pusty, zamiast tego:

s/^/\n/

Możesz to zrobić:

H;x

Spowoduje to zanieczyszczenie przestrzeni wstrzymania, więc działa tylko raz. Jednak w przypadku dwóch kolejnych bajtów można wyczyścić obszar wzorców przed zamianą, co wciąż oznacza oszczędność dwóch bajtów:

H;z;x
Jordania
źródło
1

W sed, najbliższą funkcją, którą możesz mieć, jest etykieta. Funkcja jest przydatna, ponieważ możesz wykonać jej kod wiele razy, oszczędzając w ten sposób wiele bajtów. W sednie jednak musisz podać etykietę zwrotną i jako taki nie możesz po prostu wywołać tej „funkcji” wiele razy w całym kodzie, tak jak zrobiłbyś to w innych językach.

Obejściem, którego używam, jest dodanie do jednej z dwóch pamięci flagi, która służy do wyboru etykiety zwrotnej. Działa to najlepiej, gdy kod funkcji potrzebuje tylko jednej przestrzeni pamięci (drugiej).

Przykład pokazujący, co mam na myśli: wzięty z mojego projektu, aby napisać małą grę w 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

Etykiety powinny być oczywiście golfowane tylko na jedną literę, dla lepszego wyjaśnienia użyłem pełnych nazwisk.

seshoumara
źródło
1

Puste wyrażenia regularne są równoważne z wcześniej napotkanym wyrażeniem regularnym

(podziękowania dla Riley za odkrycie tego po złożeniu anagolu )

Oto przykład, w którym mamy za zadanie utworzenie 100 @s w pustym buforze.

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

Drugie rozwiązanie jest o 1 bajt krótsze i wykorzystuje fakt, że puste wyrażenia regularne są wypełniane ostatnim napotkanym wyrażeniem regularnym. Tutaj, dla drugiego podstawienia, ostatnim wyrażeniem regularnym było .*, więc puste wyrażenie regularne zostanie tutaj wypełnione .*. Działa to również z wyrażeniami regularnymi w /conditionals/.

Zauważ, że jest to wcześniej napotkany regex, więc poniższe również by działały.

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

Puste wyrażenie regularne zostaje wypełnione @*zamiast, $ponieważ s/$/@/nigdy nie jest osiągane.

Kritixi Lithos
źródło
Tak, dobra odpowiedź. Wydłużyłem nawet wyrażenia regularne , aby można je było ponownie dopasowywać w ten sposób (dzięki czemu program jest krótszy).
Toby Speight
0

Przeważnie bezużyteczny krok:

y|A-y|B-z|

Będzie to tłumaczyć tylko Ado Bi ydo z(... i -do -), ale nic więcej, tak

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

po prostu wróci:

Hello world!

Można to zapewnić będą bezużyteczne, na próbce za pomocą tego na małymi wartościami szesnastkowym (zawierające tylko 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, elub f).

F. Hauri
źródło
2
Czy to jest coś, co znalazłeś na własnej skórze ?! ;-)
Toby Speight
Lubię niepotrzebne skrypty: sed '; ;/s/b;y|A-y|B-z|;s ;s/ //; ; ;' <<<'Hello world'(Dlaczego to nie tłumi miejsca?)
F. Hauri