Sed'de golf için ipuçları


19

Sed'de golf oynamak için hangi genel ipuçlarınız var? Ben kod-golf sorunları uygulanabilir ve aynı zamanda en azından biraz sed için spesifik olan fikirler arıyorum (örneğin "yorumları kaldırmak" bir cevap değildir).

Lütfen cevap başına bir ipucu gönderin.


4
Gerçekten bir golf ipucu değil (ama yine de golf için bir ipucu): hat beslemeleri noktalı virgül kadar bayt tüketir, böylece kodunuzu kısa ve okunabilir tutabilirsiniz .
Dennis

Bir ipucu da değil, bir sorun: GNU sed var, ama Fkomut hiç işe yaramadı. Nedenini bilen var mı?
seshoumara

@seshoumara FGNU sed (Debian testi) üzerinde çalışıyor. -Elbette stdin'den okuyorsa yazdırıyor , ancak bu bekleniyor. Ne alıyorsun sed -e 'F;Q' /etc/hostname?
Toby Speight

@TobySpeight Yani bu hatayı veriyor: char 1: unknown command: F. Belki sed güncellemeliyim; hangi versiyona sahipsin? LKomut ayrıca çalışmıyor, ancak o zamandan beri bu yararsız zaten var -l nvar. GNU sed'in sitesinde bahsedilen her şey çalışıyor.
seshoumara

1
bash, sed and dcKonuşmak ve bu diller hakkında soru sormak isteyen herkes için sohbet odasını açtım . Bir topluluk yapalım!
seshoumara

Yanıtlar:


11

Etiketleri kullanmanız gerekiyorsa , etiket adlarınızın mümkün olduğunca kısa olmasını istersiniz. Aslında en uç noktaya gelindiğinde, boş dizeyi etiket adı olarak bile kullanabilirsiniz:

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

4
GNU sed 4.3 itibariyle bu davranış kaldırıldı . :artık bir etiket gerektiriyor.
Kevin

Gerçekten de işte gerçek git kesin bağlantısı . Sanırım PPCG için bu çok fazla değişmeyecek, çünkü GNU sed 4.2.x için cevaplar göndermemize izin verildi, ancak ne yazık ki, bu hile artık resmi olarak işe yaramayacağını bilmek güzel.
seshoumara

8

GNU sed belgeler açıklanır solarak komutunu "adlı sed İsviçre Çakısı" . Ancak tüm yapmak istediğiniz bir karakterin tüm örneklerini bir başkasıyla değiştirmekse, ykomut ihtiyacınız olan şeydir:

y/a/b/

şundan kısa bir karakter:

s/a/b/g

ayrıca daha hızlıdır ve karakterleri yerinde değiştirebilir: y/12/21/
char

6

Genişletilmiş normal ifade sözdizimini (GNU sed'de) kullanmayı düşünün. -rOpsiyon puanlama bir bayt maliyeti, ancak bir çift tersbölüleri ortadan kaldırmak için sadece bir kez kullanarak \(...\)zaten kendisi için ödedi.


2
Ek not -rile GNU'ya sedözgü gibi görünüyor .
manatwork

@manat - eklendi (ancak bu bir Topluluk Wiki yanıtıdır, bu nedenle kendinizi düzenleyebilirsiniz).
Toby Speight

Elbette. Ben sadece ipucu, sadece ek bir not parçası olarak kabul etmedi.
manatwork

Ve kullanırken kendisi için ödeme tutar +, ?, {}ve |hiçbir tersbölüler ya ihtiyaç vardır çünkü, düzenli ifade maçta.
seshoumara

-Edoğru hatırlamıyorsam -rbirçok seduygulamada takma olarak çalışır .
phk

6

Bir döngüde tekrar tekrar değiştirirken:

loop:
s/foo/bar/g
tloop

döngü sonunda tüm oluşumların yerini alacağından, genel olarak değiştirmek genellikle gereksizdir:

# GNU sed
:
s/foo/bar/
t

Yukarıdaki GNU uzantısına da dikkat edin: bir etiketin boş bir adı olabilir ve daha değerli baytlar kaydedebilir. Diğer uygulamalarda, bir etiket boş olamaz ve etiket olmadan atlama komut dosyasının sonuna aktarılır (örn n. İle aynı ).


1
Boş etiket adı GNU'ya özgüdür, POSIX, komut dosyasının sonuna atlamak için argümanı olmayan dallara ihtiyaç duyar (boş bir eklemezseniz BSD ve Busybox'ta da GNU :
sed'deki

2
İsimsiz etiket, GNU sed'de bir uzantı değil, her zaman bir hataydı ve sürüm 4.3 ve sonraki sürümlerde bu hata, üzülerek düzeltildi. Buraya bakın .
seshoumara

5

Yerleşik aritmetik yoktur, ancak hesaplamalar tekli veya tekli kodlu ondalık olarak yapılabilir. Aşağıdaki kod, ondalık birimi UCD'ye dönüştürür, birim olarak x ve basamak ayırıcı olarak 0 olur:

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

ve işte ondalığa dönüştürme:

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

Bunların her ikisi de "İki sayıyı herhangi bir sayı kullanmadan çarpın" yanıtından alınır .

Sade eski tekli, bu yanıttan bu döngü çifti kullanılarak "{Kıvırcık Sayılar}"; , birimin bulunduğu yer ;. Ben vve xRoma için maç için kullandım 5ve 10; b"bis" den gelir.

# 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
... ve bunlardan herhangi birini kullanmak zorunda kalırsanız, Java cevapları ile hala rekabetçi olsa da, neredeyse kesinlikle kod golfünü kaybettiniz ;-) Yine de kullanmak eğlenceli.
Dijital Travma

Düz tekli'den ondalığa dönüştürme, ondalık form X0X'in tekli giriş eşdeğeri için yanlış cevaplar verir, örneğin 108. Bundan sorumlu satır /[;v]/!s/\b/0/2, /[;v]/!s:x\+:&0:çalışması için değiştirilmesi gerekir . Buraya bakın .
seshoumara

@seshoumara, bağlantınız boş bir sayfa gibi görünüyor. Ancak bu kodu referans verilen yanıttan ayıklarken bir hata yaptığım tamamen mantıklı, bu yüzden sadece düzeltmenizi uygulayacağım.
Toby Speight

Bağlantı doğru yükleniyor, ancak "TIO" ile gri bir sayfadan başka bir şey bekliyordum ve Ubuntu logosuna benzeyen bir şey - amaçlanan şey bu mu? Ve ben ( 58007 ) referans verdiğim cevapların ikincisine atıfta bulunuyordum, çünkü o kadar basit olan tek örneklemin kaynağı budur.
Toby Speight

TIO bağlantısında düzeltilmiş kodun yanı sıra 108 örnek bir giriş de bulunmalıdır. Kodu çalıştırırken, şimdi sabit kod satırının daha önce oluşturduğu gibi 180 değil, doğru sonucu 108 görmüş olmalısınız. Referans verilen cevabı güncellemek tamamen size bağlıdır. Bu bir topluluk wiki'si.
seshoumara

4

man sed(GNU) 'da belirtildiği gibi , herhangi bir karakteri sözdizimini kullanarak normal ifadeler için sınırlayıcı olarak kullanabilirsiniz

\%regexp%

nerede % herhangi bir karakter için bir yer tutucudur.

Bu, aşağıdaki gibi komutlar için kullanışlıdır

/^http:\/\//

bunlar daha kısa

\%^http://%

Ne belirtilen GNU manuel sed ama değil deman sed size sınırlayıcıları değiştirebilir olmasıdır s///ve y///de.

Örneğin, komut

ss/ssg

desen alanındaki tüm eğik çizgileri kaldırır.


4

Soru açıkça yasaklanmadıysa, bu meta soru için fikir birliği , sayısal girdinin tek başına olabileceğidir. Bu, bu cevaba göre size 86 bayt ondalık ondalık kaydeder .


Sed için düz eski tek biçimli biçime atıfta bulunan bu meta konsensüs değil mi? Her iki durumda da UCD'deki bir girdinin bana yardımcı olacağı birkaç cevabım var.
seshoumara

@seshoumara UCD değil, tekli demek istedim
Digital Trauma

Daha sonra ondalıktan düz eski tekere dönüştürme, bağladığınız cevaba göre size 126 bayt kazandırır. 86 bayt UCD'ye dönüştürme içindir.
seshoumara

4

Bu ipucu cevabına göre , ondalık ve düz tek sayı sayıları arasındaki dönüşümlerle ilgili olarak, aşağıdaki alternatif yöntemleri, avantajları ve dezavantajları ile sunuyorum.

Ondalıktan düze tekli: 102 + 1 (r bayrağı) = 103 bayt. Sayılan \t1 bayt, bir hazır sekme olarak.

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

Çevrimiçi deneyin!

Avantajı: 22 bayt daha kısa ve ekstra, giriş olarak negatif tamsayılarla çalışır

Dezavantajı: tutma alanının üzerine yazar. Bununla birlikte, giriş tamsayısını doğrudan programın başında dönüştürmeniz gerektiğinden, bu sınırlama nadiren hissedilir.

Düz tekli ila ondalık: 102 + 1 (r bayrağı) = 103 bayt

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

Çevrimiçi deneyin!

Avantajı: 14 bayt daha kısadır. Bu kez her iki uç versiyonu da negatif tamsayılar için girdi olarak çalışır.

Dezavantajı: tutma alanının üzerine yazar

Karmaşık bir meydan okuma için, bu parçacıkları, dönüştürülecek sayının yanı sıra desen alanında veya tutma alanında bulunabilecek diğer bilgilerle çalışacak şekilde uyarlamanız gerekir. Yalnızca pozitif sayılarla çalıştığınızı veya yalnızca sıfırın geçerli bir giriş / çıkış olmayacağını biliyorsanız kod daha fazla golf edilebilir.

Bu snippet'leri oluşturup kullandığım, böyle bir meydan okuma cevabı örneği, bir sayının Karşılıklılığıdır (1 / x) .


Tekli-to-ondalık için son iki oyuncu değişikliği birleştirerek iki bayt kaydedebilirsiniz: s:\n|@$::g. tio.run/##K05N@f@/2ErX3krNwIpL30G/…
Ürdün

Ben ondalık dönüştürücü tek denemek kendi denedim. İşte 97 bayt :) Çevrimiçi deneyin! (ayrıca gerektirmez -r, ancak yeni fikir birliği ile, bayraklar zaten bayt hesabına dahil edilmez ve tutma alanını
bozmaz

Aslında son satırı olarak /\n/tadeğiştirirseniz /\n/t, 96
Kritixi Lithos

@Cowsquack Teşekkürler, 96 harika! Şimdi vaktiniz yok, bu hafta sonu ona bakacağız.
seshoumara

Tabii, o zaman sohbet bana bir ping göndermek :)
Kritixi Lithos

3

tVe Tkomutları hakkında konuşalım , man sayfasında açıklansalar da, özellikle kod karmaşıklaştığında, unutmak ve yanlışlıkla hataları tanıtmak kolaydır.

Man sayfası beyanı t:

A s///, son giriş satırı okunduğundan ve son t veya T komutundan bu yana başarılı bir değişiklik yaptıysa , etiketlemek için dallayın.

Ne demek istediğimi gösteren örnek: Diyelim ki bir sayı listeniz var ve kaç tane negatif olduğunu saymak istiyorsunuz. Aşağıdaki kısmi kod:

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

Tamam görünüyor, ama değil. İlk sayı pozitifse, bu kod yine de negatif olduğunu düşünecektir, çünkü sayacı başlattığımızda tbaşarılı bir sikame olduğu için, ilk girdi satırı için yapılan atlama ne olursa olsun gerçekleştirilir ! Doğru olan: /-/b increment_counter.

Bu kolay görünüyorsa, işlevleri simüle etmek için birden fazla atlama yaparken hala kandırabilirsiniz. Örneğimizde increment_counterkesinlikle kod bloğu çok sayıda skomut kullanacaktır . İle geri dönmek b main, "ana" deki başka bir kontrolün aynı tuzağa düşmesine neden olabilir. Bu yüzden genellikle kod blokları ile dönmek s/.*/&/;t label. Çirkin ama kullanışlı.


2

Desen alanını ile temizlemek yerine, GNU sed ile giderseniz (küçük harf) komutunu s/.*//kullanın z. Düşük bayt sayımının yanı sıra, komutun dyaptığı gibi bir sonraki döngüye başlamaması avantajına sahiptir , bu da bazı durumlarda yararlı olabilir.


1
Geçersiz çok baytlı dizileriniz (eşleşmeyen .) varsa da yararlı olabilir .
Toby Speight

2

Bunun eski bir iş parçacığı olduğunu biliyorum, ama UCD dönüştürücülerine bu beceriksiz ondalığı buldum, neredeyse yüz baytla, bazıları bile tutma alanını bozuyor veya özel hatalı sedsürümler gerektiriyor .

İçin UCD için ondalık ben kullanım (68 byte; eski en iyi burada yayınlanan 87 bayt)

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

Ondalık UCD (ayrıca 66 bayt; burada en iyi yayınlanan 96)

s/$/\n0123456789/
:a      
s/\([0-8]\)x\(.*\n.*\)\1\(.\)/\3\2\1\3/
ta      
P;d
  • \nyerine taşınabilir değildir. Bunun yerine farklı bir karakter kullanabilir ve iki bayt kaydedebilirsiniz, ancak eki kaldırmak için daha fazla bayt gerekir P;d; sonraki açıklamaya bakınız. Veya, tutma alanınız boşsa G;s/$/9876543210/bayt cezası olmadan yapın.
  • Daha fazla işleme ihtiyacınız varsa, s/\n.*//yerine daha fazla bayt gerekir P;d.
  • Bu eski GNU sedsürümleri için her biri iki bayt tasarruf edebilirsiniz
  • Hayır, genişletilmiş düzenli ifadeler geri başvurma yapmadığından bu altı ters eğik çizgiyi kaydedemezsiniz

Bu iş parçacığında yayınlanan ve boşluk bırakan veya hatalı sed sürümlerine ihtiyaç duyan UCD ve geri dönüştürücülerde ondalık sayı yoktur.
seshoumara

6 Nisan'dan itibaren kendi yanıtınız altın alanı kullanıyor ve yalnızca sedPOSIX standardını ihlal eden eski sürümlerle çalışacak .
Philippos

UCD dönüşümlerine ondalık yapmıyorum! Konuyu dikkatlice tekrar okuyun. UCD, 12'nin 0x0xx'a (cevabınızın hesapladığı şey), düz tekli (cevabımın hesapladığı şey) 12'nin xxxxxxxxxxxx'e dönüştürüldüğü anlamına gelir. @ Sembolünü seçtim, ama fikri anladınız. Dahası, PPCG'de birinin POSIX standardına uyması gerekmez.
seshoumara

Sizi memnun ederse, şerif
Philippos

2

Tüm girişi aynı anda okuyun -z

Genellikle tüm girdi üzerinde bir seferde bir satır yerine bir defada çalışmanız gerekir. NKomut bunun için yararlıdır:

:
$!{N;b}

... ancak genellikle atlayıp -zbayrağını kullanabilirsiniz .

-zBayrak sed kullanım NUL (kılan \0yerine kendi giriş hattı ayırıcı olarak) \nEğer giriş içermez biliyorum eğer öyleyse, \0bir zamanlar tek bir “hat” gibi en girişin tüm okuyacak:

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

Çevrimiçi deneyin!


2

Bir bayta yeni satır ekle

GKomut Bekletme uzay bunun yerine boş eğer öyleyse, bir yeni satır ve desen uzaya tutma alanı içeriğini ekler:

s/$/\n/

Bunu yapabilirsiniz:

G

Üç baytlık bir satırsonu ekle

HKomut Yeni satır ve tutma boşluğuna desen alanının içeriğini ekler ve xBekletme uzay bunun yerine boş eğer öyleyse, iki swapları:

s/^/\n/

Bunu yapabilirsiniz:

H;x

Bu tutma alanınızı kirletecektir, bu yüzden sadece bir kez çalışır. Bununla birlikte, iki bayt daha için, takas etmeden önce desen alanınızı temizleyebilirsiniz;

H;z;x

1

Sed'de sahip olabileceğiniz bir işleve en yakın şey bir etikettir. Bir işlev yararlıdır, çünkü kodunu birden çok kez çalıştırabilir, böylece çok fazla bayt kaydedebilirsiniz. Sed'de dönüş etiketini belirtmeniz gerekir ve bu nedenle bu "işlevi" kodunuz boyunca diğer dillerde yaptığınız gibi birçok kez çağıramazsınız.

Kullandığım geçici çözüm, iki anıdan birine dönüş etiketi seçmek için kullanılan bir bayrak eklemektir. Bu, işlev kodu yalnızca tek bir bellek alanına (diğeri) ihtiyaç duyduğunda en iyi sonucu verir.

Ne demek istediğimi gösteren örnek: sed'de küçük bir oyun yazmak için bir projemden alındı

# 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

Etiketler elbette sadece bir harfe golf edilmelidir, daha iyi bir açıklama için tam adları kullandım.


1

Boş normal ifadeler, daha önce karşılaşılan normal ifadeye eşdeğerdir

( Bunu anagol gönderisinden keşfettiği için Riley'ye teşekkürler )

İşte @boş bir arabellekte 100 sn oluşturmakla görevlendirildiğimiz bir örnek .

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

İkinci çözüm 1 byte daha kısadır ve boş regex'lerin son karşılaşılan regex ile doldurulması gerçeğini kullanır. Burada, ikinci ikame için, son normal ifade .*, bu yüzden buradaki boş normal ifade ile doldurulacaktır .*. Bu, içindeki normal ifadelerle de çalışır /conditionals/.

Daha önce karşılaşılan normal ifade olduğunu unutmayın, bu nedenle aşağıdakiler de işe yarayacaktır.

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

Boş regex @*yerine asla ulaşılmaz $çünkü doldurulur s/$/@/.


Evet, iyi cevap. Hatta regexes daha uzun yaptım böylece böylece bu şekilde yeniden eşleştirilebilir (böylece programı kısaltmak).
Toby Speight

0

Çoğunlukla yararsız adım:

y|A-y|B-z|

Bu yalnızca çevirecek Akadar Bve yhiç z(... ve -hiç -;), ama başka bir şey, bu yüzden

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

sadece geri dönecek:

Hello world!

Bu küçük harfle onaltılık değerler bu kullanarak örnek için, yararsız olacaktır emin olabilir (sadece içeren 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, eveya f.)


2
Bu zor yoldan bulduğun bir şey mi ?! ;-)
Toby Speight

Yararsız komut gibi: sed '; ;/s/b;y|A-y|B-z|;s ;s/ //; ; ;' <<<'Hello world'(Neden bu do not alan bastırmak?)
F. Hauri
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.