Tips untuk bermain golf di sed


19

Apa tips umum yang Anda miliki untuk bermain golf? Saya mencari ide yang dapat diterapkan untuk masalah kode-golf dan yang juga setidaknya agak spesifik untuk sed (mis. "Hapus komentar" bukan jawaban).

Silakan kirim satu tip per jawaban.


4
Bukan benar-benar tip golf (tapi masih tip untuk golf): linefeeds mengkonsumsi byte sebanyak semikolon, sehingga Anda dapat menjaga kode Anda pendek dan mudah dibaca.
Dennis

Bukan tip juga, tetapi masalah: Saya memiliki GNU sed, namun Fperintahnya tidak pernah bekerja. Adakah yang tahu mengapa?
seshoumara

@seshoumara Fbekerja pada sed GNU saya (pengujian Debian). Itu hanya mencetak -jika membaca dari stdin, tentu saja, tapi itu diharapkan. Apa yang Anda dapatkan dari sed -e 'F;Q' /etc/hostname?
Toby Speight

@TobySpeight Yang memberikan kesalahan ini: char 1: unknown command: F. Saya harus memperbarui sed mungkin; Versi apa yang kau miliki? The Lperintah juga tidak bekerja, tapi itu tetap berguna karena -l nada. Segala sesuatu yang disebutkan di situs sed GNU berfungsi.
seshoumara

1
Saya membuka ruang obrolan bash, sed and dcuntuk semua yang ingin berbicara dan bertanya tentang bahasa-bahasa ini. Ayo buat komunitas!
seshoumara

Jawaban:


11

Jika Anda perlu menggunakan label maka pasti Anda ingin nama label Anda sesingkat mungkin. Bahkan diambil ekstrem, Anda bahkan dapat menggunakan string kosong sebagai nama label:

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

4
Pada gnu sed 4.3, perilaku ini telah dihapus . :sekarang membutuhkan label.
Kevin

Memang, di sini juga ada tautan komit git yang sebenarnya . Saya kira untuk PPCG ini tidak akan banyak berubah, karena kami diizinkan memposting jawaban untuk GNU sed 4.2.x, tetapi ada baiknya untuk mengetahui, meskipun sayangnya, bahwa trik ini tidak akan berfungsi lagi secara resmi.
seshoumara

8

The dokumentasi GNU sed menggambarkan sperintah sebagai "sed ini Swiss Army Knife" . Tetapi jika semua yang Anda ingin lakukan adalah mengganti semua instance dari satu karakter dengan yang lain, maka yperintahnya adalah apa yang Anda butuhkan:

y/a/b/

satu karakter lebih pendek dari:

s/a/b/g

ini juga jauh lebih cepat, dan dapat menukar karakter di tempatnya:y/12/21/
mikeserv

6

Pertimbangkan untuk menggunakan sintaks regex yang diperluas (dalam GNU sed). The -rpilihan biaya satu byte dalam mencetak gol, tapi menggunakannya hanya sekali untuk menghilangkan backslashes dari sepasang \(...\)sudah membayar untuk dirinya sendiri.


2
Dengan catatan tambahan yang -rtampaknya sedspesifik untuk GNU .
manatwork

@manat - ditambahkan (tetapi ini adalah jawaban Wiki Komunitas, jadi Anda bisa mengedit sendiri).
Toby Speight

Tentu saja. Saya hanya tidak menganggapnya sebagai bagian dari tip, hanya catatan tambahan.
manatwork

Dan itu terus membayar untuk dirinya sendiri ketika menggunakan +, ?, {}dan |dalam pertandingan regex, karena tidak ada backslashes diperlukan baik.
seshoumara

-Eberfungsi sebagai alias untuk -rbanyak sedimplementasi jika saya ingat dengan benar.
phk

6

Saat berulang kali mengganti dalam satu lingkaran:

loop:
s/foo/bar/g
tloop

biasanya tidak perlu diganti secara global, karena loop pada akhirnya akan mengganti semua kejadian:

# GNU sed
:
s/foo/bar/
t

Perhatikan juga ekstensi GNU di atas: label dapat memiliki nama kosong, menyimpan byte yang lebih berharga. Dalam implementasi lain, label tidak boleh kosong, dan melompat tanpa label mentransfer aliran ke akhir skrip (yaitu sama dengan n).


1
Nama label kosong adalah GNU-spesifik, POSIX membutuhkan cabang tanpa argumen untuk melompat ke akhir skrip (tampaknya perilaku di BSD dan Busybox, juga di sed GNU jika Anda tidak menambahkan kosong :)
ninjalj

2
Label tanpa nama selalu menjadi bug di GNU, bukan ekstensi, dan dalam versi 4.3 dan lebih tinggi bug ini, sayangnya, diperbaiki. Lihat di sini .
seshoumara

5

Tidak ada aritmatika bawaan, tetapi perhitungan dapat dilakukan dalam unimal atau dalam desimal berkode unary. Kode berikut mengkonversi desimal ke UCD, dengan x sebagai unit dan 0 sebagai pemisah digit:

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

dan inilah konversi kembali ke desimal:

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

Keduanya diambil dari jawaban hingga "Kalikan dua angka tanpa menggunakan angka apa pun" .

Unary tua biasa dapat dikonversi menggunakan pasangan loop ini dari jawaban ini ke "{Nomor Keriting};" , dimana unit berada ;. Saya telah menggunakan vdan xuntuk mencocokkan Romawi untuk 5dan 10; bberasal dari "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
... dan jika Anda harus menggunakan salah satu dari ini, Anda hampir pasti sudah kehilangan kode golf, meskipun Anda mungkin masih bisa bersaing dengan jawaban Java ;-) Tetap menyenangkan untuk digunakan.
Trauma Digital

Konversi dari sederhana unary ke desimal memberikan jawaban yang salah untuk input unary setara dengan bentuk desimal X0X, misalnya 108. Baris yang bertanggung jawab untuk ini adalah /[;v]/!s/\b/0/2, yang perlu diubah agar /[;v]/!s:x\+:&0:dapat berfungsi. Lihat di sini .
seshoumara

@seshoumara, tautan Anda tampaknya halaman kosong. Tapi itu sepenuhnya masuk akal bahwa saya membuat kesalahan ketika mengekstraksi kode itu dari jawaban yang direferensikan, jadi saya hanya akan menerapkan perbaikan Anda.
Toby Speight

Tautan memuat dengan benar, tapi saya mengharapkan sesuatu selain halaman abu-abu dengan "TIO" dan sesuatu yang tampak seperti logo Ubuntu - apakah itu yang dimaksudkan? Dan saya merujuk pada jawaban kedua yang saya referensikan ( 58007 ), karena dari situlah sampel polos-unary berasal.
Toby Speight

TIO link seharusnya berisi kode yang diperbaiki, ditambah contoh input, 108 di unary. Saat menjalankan kode, Anda seharusnya melihat hasil yang benar 108, dan bukan 180, seperti yang sebelumnya dihasilkan oleh baris kode yang sekarang diperbaiki. Memperbarui jawaban yang dirujuk sepenuhnya terserah Anda. Ini adalah wiki komunitas.
seshoumara

4

Seperti disebutkan dalam man sed (GNU), Anda dapat menggunakan karakter apa pun sebagai pembatas untuk ekspresi reguler dengan menggunakan sintaks

\%regexp%

di mana %placeholder untuk karakter apa pun.

Ini berguna untuk perintah seperti

/^http:\/\//

yang lebih pendek sebagai

\%^http://%

Apa yang disebutkan dalam manual sed GNU tetapi tidak dalam man sedadalah bahwa Anda dapat mengubah pembatass/// dan y///juga.

Misalnya, perintahnya

ss/ssg

menghapus semua garis miring dari ruang pola.


4

Jika tidak secara eksplisit dilarang oleh pertanyaan, konsensus untuk pertanyaan meta ini adalah bahwa input numerik mungkin unary. Ini menghemat 86 byte desimal ke unary sesuai jawaban ini .


Bukankah itu konsensus meta untuk merujuk pada format lama unary? Saya punya beberapa jawaban di mana input dalam UCD akan membantu saya, kalau-kalau itu baik.
seshoumara

@seshoumara yang saya maksud adalah unary, bukan UCD
Digital Trauma

Kemudian konversi dari desimal ke unary biasa menghemat 126 byte sesuai jawaban yang Anda tautkan. 86 byte adalah untuk konversi ke UCD.
seshoumara

4

Memperluas hal ini jawaban tip , berkenaan dengan konversi antara format angka desimal dan sederhana, saya menyajikan metode alternatif berikut, dengan kelebihan dan kekurangannya.

Desimal ke polos unary: 102 + 1 (r flag) = 103 byte. Saya dihitung \tsebagai tab literal, sebagai 1 byte.

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

Cobalah online!

Keuntungan: 22 byte lebih pendek dan ekstra, berfungsi dengan bilangan bulat negatif sebagai input

Kerugian: itu menimpa ruang penahanan. Namun, karena kemungkinan Anda perlu mengonversi bilangan bulat input tepat di awal program, batasan ini jarang dirasakan.

Biasa unary ke desimal: 102 + 1 (r flag) = 103 byte

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

Cobalah online!

Keuntungan: lebih pendek 14 byte. Kali ini kedua versi tip berfungsi untuk bilangan bulat negatif sebagai input.

Kerugian: itu menimpa ruang penahanan

Untuk tantangan yang rumit, Anda harus menyesuaikan cuplikan ini agar berfungsi dengan informasi lain yang mungkin ada di ruang pola atau ruang penyimpanan, di samping nomor yang akan dikonversi. Kode dapat di-golf-kan lebih banyak, jika Anda tahu Anda hanya bekerja dengan angka positif atau nol saja tidak akan menjadi input / output yang valid.

Contoh jawaban tantangan seperti itu, tempat saya membuat dan menggunakan cuplikan ini, adalah Timbal Balik dari angka (1 / x) .


Untuk unary-to-desimal Anda dapat menyimpan dua byte dengan menggabungkan dua pergantian pemain terakhir: s:\n|@$::g.tio.run/##K05N@f@/2ErX3krNwIpL30G/…
Jordan

Saya sudah mencoba sendiri di desimal ke konverter unary. Inilah 97 byte :) Cobalah secara online! (juga tidak memerlukan -r, tetapi dengan konsensus baru, bendera tidak diperhitungkan dengan bytecount , dan itu tidak mengacaukan ruang penahanan)
Kritixi Lithos

Sebenarnya jika Anda mengubah baris terakhir dari /\n/tamenjadi/\n/t , Anda menghemat 1 byte untuk mendapatkan 96
Kritixi Lithos

@Cowsquack Terima kasih, 96 sangat bagus! Tidak punya waktu sekarang, akan melihatnya akhir pekan ini.
seshoumara

Tentu, tolong kirimkan saya ping saat ngobrol :)
Kritixi Lithos

3

Mari kita bicarakan tdan Tperintah, bahwa meskipun mereka dijelaskan di halaman manual, mudah untuk melupakannya dan memperkenalkan bug secara tidak sengaja, terutama ketika kode menjadi rumit.

Pernyataan halaman manual untuk t:

Jika a s///telah melakukan substitusi yang berhasil sejak baris input terakhir dibaca dan sejak perintah t atau T terakhir, maka cabang untuk memberi label.

Contoh yang menunjukkan apa yang saya maksud: Katakanlah Anda memiliki daftar angka dan Anda ingin menghitung berapa banyak negatif. Kode parsial di bawah ini:

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

Terlihat oke, tapi ternyata tidak. Jika angka pertama positif, kode itu masih akan berpikir itu negatif, karena lompatan dilakukan melalui tuntuk input baris pertama dilakukan terlepas, karena ada ssubstitusi yang berhasil ketika kami menginisialisasi penghitung! Yang benar adalah: /-/b increment_counter.

Jika ini tampak mudah, Anda masih bisa tertipu ketika melakukan beberapa lompatan bolak-balik untuk mensimulasikan fungsi. Dalam contoh kitaincrement_counter blok kode pasti akan menggunakan banyak sperintah. Kembali dengan b mainmungkin menyebabkan cek lain "utama" jatuh dalam perangkap yang sama. Itu sebabnya saya biasanya kembali dari blok kode dengan s/.*/&/;t label. Itu jelek, tapi berguna.


2

Alih-alih membersihkan ruang pola dengan s/.*//, gunakan zperintah (huruf kecil) jika Anda menggunakan GNU sed. Selain jumlah byte yang lebih rendah, ia memiliki keuntungan bahwa ia tidak akan memulai siklus berikutnya seperti perintah d, yang dapat berguna dalam situasi tertentu.


1
Mungkin juga bermanfaat jika Anda memiliki urutan multi-byte yang tidak valid (yang tidak cocok dengan .).
Toby Speight

2

Saya tahu ini adalah utas lama, tapi saya baru saja menemukan desimal kikuk untuk konverter UCD, dengan hampir seratus byte, beberapa bahkan mengacaukan ruang penahanan atau membutuhkan kesalahan khusus sed versi .

Untuk desimal ke UCD saya menggunakan (68 byte; sebelumnya terbaik diposting di sini 87 byte)

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

UCD ke desimal adalah (juga 66 byte; sebelumnya terbaik diposting di sini 96)

s/$/\n0123456789/
:a      
s/\([0-8]\)x\(.*\n.*\)\1\(.\)/\3\2\1\3/
ta      
P;d
  • \ndalam penggantian tidak portabel. Anda dapat menggunakan karakter yang berbeda sebagai gantinya dan menyimpan dua byte, tetapi Anda akan membutuhkan lebih banyak byte untuk menghapus lampiran alih-alihP;d ; lihat komentar selanjutnya. Atau, jika ruang tunggu Anda kosong, lakukan G;s/$/9876543210/tanpa penalti byte.
  • Jika Anda perlu diproses lebih lanjut, Anda akan memerlukan beberapa byte lagi untuk s/\n.*// ganti P;d.
  • Anda bisa menyimpan masing-masing dua byte untuk GNU tua yang buggy itu sed versi-versi
  • Tidak, Anda tidak dapat menyimpan enam garis miring terbalik karena ekspresi reguler yang diperluas tidak melakukan referensi balik

Tidak ada desimal untuk UCD dan konverter belakang diposting di utas ini yang mengacaukan ruang penahanan atau memerlukan versi yang salah.
seshoumara

Jawaban Anda sendiri mulai 6 April menggunakan ruang emas dan hanya akan berjalan dengan sedversi lama yang melanggar standar POSIX.
Philippos

Saya tidak melakukan konversi desimal ke UCD! Baca utas lagi dengan cermat. UCD berarti 12 dikonversi ke 0x0xx (apa yang dihitung jawaban Anda), sedangkan polos unary (apa yang dihitung jawaban saya) berarti 12 dikonversi ke xxxxxxxxxxxx. Saya memilih @ sebagai simbol, tetapi Anda mendapatkan idenya. Dan lebih lanjut, pada PPCG kita tidak perlu mematuhi standar POSIX.
seshoumara

Jika itu menyenangkan Anda, sheriff
Philippos

2

Baca seluruh input sekaligus dengan -z

Seringkali Anda perlu mengoperasikan seluruh input sekaligus alih-alih satu baris sekaligus. The NPerintah ini berguna untuk itu:

:
$!{N;b}

... tetapi biasanya Anda dapat melewatinya dan menggunakan -zbendera sebagai gantinya.

The -zflag membuat sed penggunaan NUL ( \0) sebagai pemisah jalur input bukan \n, jadi jika Anda tahu masukan Anda tidak akan berisi \0, itu akan membaca semua masukan sekaligus sebagai “garis” tunggal:

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

Cobalah online!


2

Tambahkan baris baru dalam satu byte

The Gperintah menambahkan baris baru dan isi ruang terus ke ruang pola, jadi jika ruang hold Anda kosong, bukan ini:

s/$/\n/

Kamu bisa melakukan ini:

G

Tambahkan baris baru dalam tiga byte

The Hperintah menambahkan baris baru dan isi ruang pola ruang ditahan, dan xswap dua, jadi jika ruang hold Anda kosong, bukan ini:

s/^/\n/

Kamu bisa melakukan ini:

H;x

Ini akan mencemari ruang penahanan Anda, jadi hanya berfungsi sekali. Namun, untuk dua byte lagi, Anda dapat menghapus ruang pola Anda sebelum bertukar, yang masih merupakan penghematan dua byte:

H;z;x

1

Selain itu, hal terdekat dengan fungsi yang dapat Anda miliki adalah label. Suatu fungsi berguna karena Anda dapat mengeksekusi kodenya beberapa kali, sehingga menghemat banyak byte. Namun, Anda harus menentukan label kembali dan karena itu Anda tidak bisa begitu saja memanggil "fungsi" ini beberapa kali di seluruh kode Anda seperti cara Anda melakukannya dalam bahasa lain.

Solusi yang saya gunakan adalah menambahkan salah satu dari dua kenangan bendera, yang digunakan untuk memilih label kembali. Ini bekerja paling baik ketika kode fungsi hanya membutuhkan satu ruang memori (yang lain).

Contoh menunjukkan apa yang saya maksud: diambil dari proyek saya untuk menulis permainan kecil di 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

Label-label itu harus golf, tentu saja hanya untuk satu huruf, saya menggunakan nama lengkap untuk penjelasan yang lebih baik.


1

Regex kosong setara dengan regex yang ditemukan sebelumnya

(Terima kasih kepada Riley karena menemukan ini dari pengajuan anagol )

Berikut ini adalah contoh di mana kami ditugaskan untuk membuat 100 @dalam buffer kosong.

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

Solusi kedua adalah 1 byte lebih pendek dan menggunakan fakta bahwa regex kosong diisi dengan regex yang terakhir ditemui. Di sini, untuk substitusi kedua, regex terakhir adalah .*, sehingga regex kosong di sini akan diisi .*. Ini juga bekerja dengan regex di/conditionals/ .

Perhatikan bahwa ini adalah regex yang sebelumnya ditemui , jadi yang berikut ini juga akan berfungsi.

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

Regex kosong diisi @*alih - alih$ karena s/$/@/tidak pernah tercapai.


Ya, jawaban yang bagus. Saya bahkan membuat regex lebih lama sehingga bisa dicocokkan ulang seperti ini (sehingga membuat program lebih pendek).
Toby Speight

0

Langkah yang paling tidak berguna:

y|A-y|B-z|

Ini hanya akan menerjemahkan Ake Bdan yke z(... dan -ke- ;), tetapi tidak ada yang lain, jadi

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

hanya akan kembali:

Hello world!

Anda bisa memastikan hal ini akan sia-sia, untuk sampel dengan menggunakan ini pada kasus yang lebih rendah nilai heksadesimal (yang hanya berisi 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, eatau f.)


2
Apakah ini sesuatu yang Anda temukan dengan cara yang sulit ?! ;-)
Toby Speight

Saya suka skrip yang tidak berguna: sed '; ;/s/b;y|A-y|B-z|;s ;s/ //; ; ;' <<<'Hello world'(Mengapa ini tidak menekan ruang?)
F. Hauri
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.