Поради для гольфу в седі


19

Які загальні поради щодо гольфу в седі? Я шукаю ідеї, які можна застосувати до проблем із кодом-гольфу, які також принаймні дещо характерні для sed (наприклад, "видалити коментарі" - це не відповідь).

Будь ласка, опублікуйте одну пораду за кожну відповідь.


4
Насправді не підказка для гольфу (але все-таки підказка для гольфу): лінійки харчування споживають стільки ж байтів, скільки крапки з комою, так що ви можете тримати код коротким і читабельним.
Денніс

Не підказка, але проблема: у мене є GNU sed, але Fкоманда ніколи не працювала. Хтось знає, чому?
seshoumara

@seshoumara Fпрацює на моєму GNU sed (тестування Debian). Він просто друкує, -якщо читає зі stdin, звичайно, але це очікувано. Що ви отримуєте sed -e 'F;Q' /etc/hostname?
Toby Speight

@TobySpeight Це дає цю помилку: char 1: unknown command: F. Я маю оновити sed, можливо; яка у вас версія? LКоманда також не працює, але це марно , так як в будь-якому випадку -l nіснує. Все інше, що згадується на сайті GNU sed, працює.
seshoumara

1
Я відкрив кімнату чату bash, sed and dcдля всіх, хто хоче поговорити і запитати про ці мови. Зробимо спільноту!
seshoumara

Відповіді:


11

Якщо вам потрібно використовувати мітки, то точно ви хочете, щоб назви ваших ярликів були якомога коротшими. Насправді до крайньої мети ви можете навіть використовувати порожню рядок як назву мітки:

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


Дійсно, ось також фактичне посилання git commit . Я думаю, що для PPCG це не сильно зміниться, оскільки нам дозволяється публікувати відповіді на GNU sed 4.2.x, але добре знати, хоча, на жаль, цей трюк більше не буде офіційно працювати.
seshoumara

8

Документація GNU sed описує sкоманду як "нож швейцарської армії" . Але якщо все, що ви хочете зробити, це замінити всі екземпляри одного символу іншим, тоді yвам потрібно:

y/a/b/

на один знак коротший, ніж:

s/a/b/g

це також шлях швидше, і можна поміняти символи на місці:y/12/21/
mikeserv

6

Розглянемо використання синтаксису розширеного регулярного виразів (у sed GNU). -rВаріант варто один байт у виграші, але використовувати його тільки один раз , щоб виключити зворотний слеш з пари \(...\)вже заплатив за себе.


2
З додатковою запискою, яка, -rздається, є sedспецифічною для GNU .
манатура

@manat - додано (але це відповідь у спільноті Wiki, тому ви могли самі редагувати).
Toby Speight

Звичайно. Я просто не вважав це частиною підказки, лише додатковою запискою.
манатура

І він продовжує платити за себе при використанні +, ?, {}і |в регулярному виразі відповідає, так як немає зворотних слеша не потрібно ні.
seshoumara

-Eпрацює як псевдонім для -rбагатьох sedреалізацій, якщо я правильно пам’ятаю.
phk

6

При повторній заміні в циклі:

loop:
s/foo/bar/g
tloop

зазвичай замінювати глобально, оскільки цикл з часом замінить усі події:

# GNU sed
:
s/foo/bar/
t

Зверніть увагу також на розширення GNU, наведене вище: мітка може мати порожнє ім’я, економлячи більш дорогоцінні байти. В інших реалізаціях мітка не може бути порожньою, а стрибки без мітки переносять потік в кінець сценарію (тобто те саме, що n).


1
Порожня назва мітки специфічна для GNU, POSIX вимагає гілок без аргументу, щоб перейти до кінця сценарію (схоже, це поведінка в BSD і Busybox, також у GNU sed, якщо ви не додасте порожнє :)
ninjalj

2
Безіменна мітка завжди була помилкою в GNU sed, а не розширенням, а у версії 4.3 та вище цю помилку, на жаль, виправили. Дивіться тут .
сешомара

5

Немає вбудованої арифметики, але обчислення можна проводити в одинарній чи в одній кодовій формі. Наступний код перетворює десятковий в UCD, з одиницею x і 0 як роздільник цифр:

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

і ось повернення до десяткового:

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

Вони обидва взяті з відповіді "Помножте два числа, не використовуючи жодних чисел" .

Звичайний старий унар може бути перетворений за допомогою цієї пари циклів з цієї відповіді на "{Curly Numbers};" , де знаходиться одиниця ;. Я використовував vі xвідповідати Roman для 5і 10; bпоходить від "біс".

# 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
... і якщо вам доведеться використовувати будь-яке з них, ви майже напевно вже втратили гольф коду, хоча ви все ще можете бути конкурентоспроможними з відповідями Java ;-) Хоча все-таки цікаво користуватися.
Цифрова травма

Перетворення від простого одинарного до десяткового дає неправильні відповіді на одинарний еквівалент введення десяткової форми X0X, наприклад 108. Рядок, відповідальний за це /[;v]/!s/\b/0/2, повинен змінити /[;v]/!s:x\+:&0:його для роботи. Дивіться тут .
seshoumara

@seshoumara, ваше посилання видається порожньою сторінкою. Але цілком правдоподібно, що я допустив помилку під час вилучення цього коду з відповіді, що стосується, тому я просто застосую ваше виправлення.
Toby Speight

Посилання завантажується правильно, але я очікував, що щось інше, ніж сіра сторінка з "TIO" і щось, схоже на логотип Ubuntu - це те, що призначено? І я мав на увазі другу з відповідей, на яку я посилався ( 58007 ), тому що звідси походить звичайно-одинарний зразок.
Toby Speight

Посилання TIO повинно містити виправлений код, а також приклад введення, 108 уніар. При виконанні коду ви повинні бачити правильний результат 108, а не 180, як раніше генерувався тим, що тепер фіксується рядком коду. Оновлення посиланої відповіді повністю залежить від вас. Це вікі спільноти.
seshoumara

4

Як згадувалося в man sed(GNU), ви можете використовувати будь-який символ як роздільник для регулярних виразів, використовуючи синтаксис

\%regexp%

де %є заповнювач будь-якого символу.

Це корисно для таких команд

/^http:\/\//

які коротші як

\%^http://%

Те, що згадується в посібнику GNU sed, але не в man sedтому, що ви можете змінювати роздільники s///і y///так само.

Наприклад, команда

ss/ssg

видаляє всі косої риски з простору шаблону.


4

Якщо це питання явно не заборонене, то для цього мета питання є єдиною думкою , що числове введення може бути одиничним. Це дозволяє економити 86 байтів десяткових до одинакових відповідно до цієї відповіді .


Хіба це мета-консенсус щодо sed посилається на звичайний старий унарний формат? У мене є кілька відповідей, де введення в UCD допоможе мені, якщо це в будь-якому випадку.
seshoumara

@seshoumara Я мав на увазі унарний, а не UCD
Digital Trauma

Тоді перетворення від десяткового до звичайного старого одинарного заощаджує 126 байт відповідно до відповідної відповіді. 86 байт призначено для перетворення в UCD.
seshoumara

4

Розкриваючи цю підказку щодо перетворень між десятковими та простими форматами одинарних чисел, я представляю наступні альтернативні методи з їх перевагами та недоліками.

Десятковий для звичайного одинарного: 102 + 1 (r прапор) = 103 байти. Я рахував \tяк буквальну вкладку, як 1 байт.

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

Спробуйте в Інтернеті!

Перевага: він на 22 байти коротший і як додатковий, він працює з негативними цілими числами як вхідними

Недолік: він перезаписує простір утримування. Однак, оскільки більш імовірно, що вам потрібно буде конвертувати вхідне ціле число прямо на початку програми, це обмеження рідко відчувається.

Звичайна одинакова до десяткової: 102 + 1 (r прапор) = 103 байти

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

Спробуйте в Інтернеті!

Перевага: він на 14 байт коротший. На цей раз обидві версії наконечника працюють як негативні цілі числа.

Недолік: він перезаписує простір утримування

Для складного виклику вам доведеться адаптувати ці фрагменти для роботи з іншою інформацією, яка може існувати у просторі шаблону або утримувати простір, окрім числа для конвертування. Код можна збільшити в гольфі, якщо ви знаєте, що працюєте лише з додатними числами або один нуль не буде дійсним входом / виходом.

Прикладом такої відповіді на виклик, де я створив і використав ці фрагменти, є зворотний номер числа (1 / x) .


Для унарний до десяткового ви можете зберегти два байта, комбінуючи останні дві заміни: s:\n|@$::g. tio.run/##K05N@f@/2ErX3krNwIpL30G/…
Йорданія

У мене була власна спроба перетворювача від десяткового до одинарного. Ось 97 байт :) Спробуйте в Інтернеті! (також не вимагає -r, але при новому консенсусі прапори ні в якому разі не зараховуються до рахунку за рахунком , і це не
зіпсує

Насправді, якщо ви зміните останній рядок з /\n/taна /\n/t, ви економите 1 байт, щоб отримати 96
Критіксі Літос

@Cowsquack Спасибі, 96 чудово! Не встигайте зараз, будете дивитись на ці вихідні.
seshoumara

Звичайно, надішліть мені пінг-чат тоді :)
Kritixi Lithos

3

Поговоримо про команди tта Tкоманди, що хоча вони пояснені на сторінці man, про це легко забути і випадково ввести помилки, особливо коли код ускладнюється.

Заява чоловічої сторінки для t:

Якщо a s///зробила успішну заміну після прочитання останнього рядка введення та з останньої команди t або T, то відгалужуйте до мітки.

Приклад, що показує, що я маю на увазі: Скажімо, у вас є список номерів, і ви хочете порахувати, скільки негативів є. Частковий код нижче:

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

Виглядає нормально, але це не так. Якщо перше число позитивне, цей код все ще буде вважати його негативним, тому що стрибок, виконаний через tдля першого рядка введення, виконується незалежно, оскільки відбулася успішна sпідміна, коли ми ініціалізували лічильник! Правильно це: /-/b increment_counter.

Якщо це здалося простим, ви все одно можете обдурити, роблячи кілька стрибків вперед і назад, щоб імітувати функції. У нашому прикладі increment_counterблок коду напевно використовував би багато sкоманд. Повернення назад з допомогою b mainможе призвести до того, що інша реєстрація в "головному" потрапить у ту ж пастку. Ось чому я зазвичай повертаюся з кодових блоків за допомогою s/.*/&/;t label. Це некрасиво, але корисно.


2

Замість того, щоб очищати простір шаблону за допомогою s/.*//, використовуйте zкоманду (малі регістри), якщо ви переходите з GNU sed. Крім нижнього рахунку байтів, вона має перевагу в тому, що він не запустить наступний цикл, як це dробить команда , що може бути корисно в певних ситуаціях.


1
Можуть також принести користь, якщо у вас є недійсні багатобайтові послідовності (які не відповідають .).
Toby Speight

2

Я знаю, що це стара нитка, але я просто знайшов ці незграбні десяткові перетворювачі UCD з майже сотнею байтів, дехто навіть заплутав простір утримування або вимагав спеціальних несправних sed версій.

Для десятків до UCD я використовую (68 байт; колишній найкращий розміщений тут 87 байт)

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

UCD до десяткових значень (також 66 байт; колишній найкращий розміщений тут 96)

s/$/\n0123456789/
:a      
s/\([0-8]\)x\(.*\n.*\)\1\(.\)/\3\2\1\3/
ta      
P;d
  • \nв заміну не є портативним. Ви можете використовувати інший символ замість цього і зберегти два байти, але вам буде потрібно більше байтів, щоб видалити додаток замістьP;d ; див. наступне зауваження. Або, якщо місце у вашому просторі порожнє, виконайте G;s/$/9876543210/без байтового штрафу.
  • Якщо вам потрібна додаткова обробка, вам знадобиться ще кілька байтів s/\n.*// замість P;d.
  • Ви можете зберегти по два байти для тих гнучких старих GNU sed версій
  • Ні, ви не можете зберегти ці шість нахилів накиду, оскільки розширені регулярні вирази не роблять зворотних посилань

У цьому потоці немає жодних перетворювачів десятків на UCD та зворотні, які не мають місця для утримування або вимагають несправних версій sed.
seshoumara

У вашій власній відповіді від 6 квітня використовується золото пробілу і буде працювати лише зі старими sedверсіями, що порушують стандарт POSIX.
Філіппос

Я не роблю перерахунків у десятковий до UCD! Прочитайте нитку ще раз уважно. UCD означає, що 12 перетворюється на 0x0xx (те, що обчислює ваша відповідь), тоді як звичайний unry (що моя відповідь обчислює) означає, що 12 перетворюється на xxxxxxxxxxxx. Я обрав @ як символ, але ви зрозумієте, І далі, на PPCG не потрібно дотримуватися стандарту POSIX.
seshoumara

Якщо вам подобається, шерифе
Філіппос

2

Прочитайте одразу цілий ввід -z

Часто вам потрібно працювати одразу над усім входом, а не одним рядком одночасно. NКоманда корисна для цього:

:
$!{N;b}

... але зазвичай ви можете пропустити його і використовувати -zнатомість прапор.

У -zпрапорі sed використовує NUL ( \0) як свій роздільник рядків введення замість \n, тому якщо ви знаєте, що ваш вхід не буде містити \0, він буде читати весь вхід одразу як один "рядок":

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

Спробуйте в Інтернеті!


2

Додайте новий рядок в один байт

GКоманда додає нову рядок і вміст трюму в просторі картини, так що якщо тримати простір порожньо, замість цього:

s/$/\n/

Ви можете зробити це:

G

Додайте новий рядок у три байти

HКоманда додає нову рядок і вміст шаблону в трюму, і xміняє місцями два, так що якщо тримати простір порожньо, замість цього:

s/^/\n/

Ви можете зробити це:

H;x

Це забруднить ваш простір, тому воно працює лише один раз. Ще на два байти ви можете очистити простір шаблону перед тим, як поміняти, що є економією у два байти:

H;z;x

1

У sed найбільш близька функція, яку ви можете мати, - це мітка. Функція корисна тим, що ви можете виконувати її код кілька разів, тим самим економлячи багато байтів. У sed, однак, вам потрібно буде вказати мітку повернення, і, як таку, ви не можете просто викликати цю "функцію" кілька разів у всьому коді так, як ви це робили б іншими мовами.

Вирішення, яке я використовую, полягає в тому, щоб додати до одного з двох спогадів прапор, який використовується для вибору мітки повернення. Це найкраще працює, коли для коду функції потрібен лише один простір пам'яті (інший).

Приклад, що показує, що я маю на увазі: взятий з мого проекту, щоб написати невелику гру в 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

Етикетки, звичайно, повинні бути лише однією буквою, я використовував повні назви для кращого пояснення.


1

Порожні регулярні вирази еквівалентні раніше зустрічаються регулярним виразам

(спасибі Райлі за те, що він виявив це з подання анагола )

Ось приклад, коли перед нами стоїть завдання створити 100 @с у порожньому буфері.

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

Другий варіант на 1 байт коротший і використовує той факт, що порожні регулярні вирази заповнюються останнім зіткнутимся регулярним виразом. Тут для другої заміни був останній регулярний вираз .*, тому порожній регулярний вираз тут буде заповнений .*. Це також працює з регулярними виразами в /conditionals/.

Зауважте, що це раніше зустрічався регулярний вираз, тому наступне також працює.

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

Порожній реджекс заповнюється @*замість того, $що s/$/@/ніколи не досягається.


Так, гарна відповідь. Я навіть зробив регулярні вирази довше, щоб їх можна було повторно зіставити (таким чином, скорочуючи програму).
Toby Speight

0

Здебільшого марний крок:

y|A-y|B-z|

Це буде перекладатися лише Aна Bта yдо z(... і -до -;), але нічого іншого, так

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

просто повернеться:

Hello world!

Ви могли б забезпечити це буде марно, для зразка, використовуючи це на шістнадцятирічних значень нижнього регістру (що містить тільки 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, eабо f.)


2
Це те, що ви дізналися важким шляхом ?! ;-)
Toby Speight

Мені подобаються марні сценарії: sed '; ;/s/b;y|A-y|B-z|;s ;s/ //; ; ;' <<<'Hello world'(Чому це не придушує простір?)
Ф. Хаурі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.