Поради для гольфу в DC


18

Які загальні поради щодо гольфу в DC ?

DC - це утиліта для калькулятора для UNIX / Linux, що передує мові C. Мене цікавить, як зробити мої програми постійного струму (розрахунки?) Коротшими. Я шукаю ідеї, які можна застосувати до загального , які принаймні трохи специфічні для dc (наприклад, видалення коментарів не є корисною відповіддю)

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


7
Використовуйте замість Marvel.
Чарівний восьминіг Урна

Відповіді:


6

Якщо тоді, то ще

Припустимо, ми хочемо перевірити стан a==b(нехай aі bзберігаються у їх відповідно названих регістрах).

редагувати:
[         # Everything is wrapped in one big macro
  [         # An inner macro for our *then* part
              # <-- Stuff to execute if a==b here
  2Q          # Then quit the inner and outer macro
]sE       # `E' is for Execution register ;)
la lb =E  # if a==b, execute E
          # if E is executed, it will quit the whole macro, so the rest is never reached:
          # <-- Stuff to execute if a!=b here
]x        # End macro; Execute

Дозвольте (foo)бути заповнювачем для згущення:

[[(then)2Q]sE(condition)E(else)]x

Досить впевнений, що це найбільш компактний варіант, якщо це можливо (також представлений тут ).


1
Може [[thenaction]P][[elseaction]P][r]sI 2 4 =I x sI f, це старт? Дії для tehn та інших знаходяться на стеці, Iмакрос " f" замінює їх і називається умовно. тоді буде виконана верхня частина стека, і невикористаний макрос буде скинутий у I, щоб очистити стек. 2 4- лише приклад даних для порівняння. Як альтернативи [x]sIчастина може бути переміщений в порівнянні, якщо розглядати більш читабельним: [[thenaction]P][[elseaction]P] 4 4 [r]sI =I x sI f. У fприкладах просто буде показано, що стек після цього чистий ...

1
Сторінка Rosetta кодексу про Dc згадує 3 ароматів , dcі це було перша сторінка , де я бачив OpenBSD dc«s if-then-elseконструкції. Я думаю, нам потрібен dcкомплект вентиляторів з усіма 3 ароматами для всіх основних операційних систем ... o :-) ... і моя if-then-elseпропозиція вище не працює на оригіналі, dcоскільки їй бракує rкоманди ... :-(

1
А як щодо: [[(if)2Q]si(condition)i(else)]x- загортання всієї речі в макрос, а if-частини всередині іншого макросу всередині цього, щоб ви могли використати 2Qсвій вихід із усієї речі, перш ніж дійти до другої частини. Отже, якщо ви хочете зробити, якщо 1 == 1, то надрукувати ще 1 друк 2 , це було б 1[[1P2Q]si1=i2P]x(не перевірено, оскільки я не маю доступу до постійного струму прямо тут і зараз. Також був впевнений, що я зробив цю хитрість у відповіді раніше але не змогли його знайти)
daniero

Так, я зробив математику, моя пропозиція коротша. З тим самим прикладом і "нотацією", і видалення пробілів це [/*else*/]sE[[/*then*/]sE]sIlalb=IlExпроти [[/*then*/2Q]sIlalb=I/*else*/]x- 6 байт різниці. Ще неперевірене Тхо: П
Данієро

1
Приємної роботи, @daniero! Я оновлю публікацію, коли матиму час, або ви можете це зробити, якщо захочете.
Джо

5

Ви можете зберегти дані за допомогою d

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


@NoOneIsHere oh cool !!! Спасибі!
Rɪᴋᴇʀ

5

Масиви

Хоча вони головний біль для початківців, dcпропонує масиви. Вони працюють так:

value index :a    # store `value' in array linked to top of stack `a', with index `index'
      index ;a    # push a[index] on (main) stack

Як завжди, перший елемент має індекс 0. Масиви можуть бути корисні при роботі з послідовностями, як у послідовності SUDSI , особливо в поєднанні з лічильниками. Масиви можуть зменшити кількість переміщення чисел, які вам потрібно зробити (і кількість лічильників та порівнянь), якщо ви хочете вибрати певний елемент, не руйнуючи ваше оточення. Наприклад, якщо ви хочете перемістити купу маси в масив, ви можете записати рекурсивну функцію, яка використовує z(глибина стека) або z 1-як індекс, зберігає елемент і перевіряє, чи z == 0припиняється сам.

[z 1- :a z 0 !=F]dsFx    # or I could just write such a function for you :)

Будьте в курсі наступного:

  • Масиви пов’язані з екземплярами з названими стеками. Якщо ви натиснете нове значення на стек, з яким пов'язаний масив, цей масив також буде "відсунутий назад", а "новий" масив займе своє місце. Старий масив не буде корисним, доки відповідне значення в іменованому стеку також не буде придатним для використання (тобто вгорі його стека). Це складна концепція, яку краще пояснити гарною анімацією, яка поза мною.
  • Ви можете зберігати речі в іменованому масиві, фактично не натискаючи значення у відповідний названий реєстр. Однак якщо ви це зробите, ви не можете отримати доступ до стеку / зареєструватися з цим ім'ям до кінця сеансу. dcобвалиться.
  • Якщо ви виведете значення з названого стеку, будь-які значення у відповідному масиві будуть втрачені - жодних попереджень, жодних гарантій, нічого. Щойно пішов (що також може бути корисно).

Гарна робота з порадами DC!
Rɪᴋᴇʀ

dcможливо, нещодавно оновлено, і поведінка масиву може дещо змінитись щодо збоїв. Наразі не можу підтвердити жодне, але я думаю, що останній раз я використовував це в Linux.
Джо

1
Якщо ви спробуєте прочитати індекс з масиву, який не був встановлений, ви отримуєте 0, а не помилку. Що може бути дуже корисно, але також варто пам’ятати про те, якщо ви потенційно ставите 0 в масиви… Вам знадобиться інший спосіб перевірити, чи не торкнувся індекс.
brhfl

5

0 до n-ї потужності замість умовних умов / макросів

Іноді вам може знадобитися щось на зразок ternary умовно:

A == B ? C : D;

Гарний спосіб впоратися з цим описаний у відповіді @ Joe . Однак ми можемо зробити краще:

0AB-^E*C+

де E - D - C.

Це тест на рівність шляхом підняття 0 на силу різниці двох значень. Це призводить до 1, якщо дорівнює, і 0 в іншому випадку. Решта просто масштабує значення 1 або 0 до значень C або D. Це працює, тому що dcдає 0 0 = 1 і 0 n = 0 при n! = 1.


4

Іноді доводиться відкидати число зі стека. Один із способів зробити це - просто перекинути його на невикористану змінну, тобто st. Однак у деяких ситуаціях ви можете розмістити його в декількох інших місцях, наприклад, на вхідній базі, коли у вас немає більше числового вводу, або на специфікаторі точності, якщо у вас немає більше операцій, коли точність змінила б значення. У першому випадку використовуйте i. В останньому випадку використовуйте k.


Якщо числовий вихід не важливий, oйого можна також використовувати. І якщо будь-яка з цих речей неважлива, їх можна використовувати як сховище, так і просте відкидання - I/ K/ Oпригадування їх відповідно, і економить байти понад sa/ laтощо. Дійсні значення AFAIK: i2-16; kбудь-яке невід’ємне ціле число; oбудь-яке ціле число, що перевищує 1.
brhfl

4

Довжина розрахунку: Z, X, іz

Zвискакує ToS і висуває кількість цифр (десяткових), якщо це число або кількість символів, якщо це рядок. Це може бути корисно для визначення довжини результату (для буферизації виводу) або обчислення довжини рядка. Зауважте, що для чисел Zвисувається комбінована довжина цілої частини та частини дробу.

Xспливає ToS і висуває кількість цифр у частці числа. Якщо ToS був рядком, 0натискається.

Щоб знайти кількість цифр у цілій частині числа, можна скористатися dZrX-. Якщо ви не змінили точність за замовчуванням k==0, використання 1/Zкоротше, але припустимо, що вам потрібно підтримувати певну ненульову точність після операції: Kr0k1/Zrkце скоріше очі.

zвисуває кількість елементів у стеку. Одна з моїх улюблених команд, вона насправді не містить жодних значень! Він може бути використаний для генерації послідовності чисел або збільшення лічильника. Використання zdдекількох разів (скажімо, на початку макросу) може дати тестування обчислення кожного натурального чи цілого числа у порядку зростання.


Використовували zдля цього і того раніше, але мені ніколи не спадало на думку використовувати його як злом лічильника ... Відмінно…
brhfl

4

Цифри, Aякі Fпідлягають використанню замість чисел від 10 до 15. Однак вони все ж повинні розглядатися ефективно як базові 10 цифри (якщо вхідна база дорівнює 10) у різних місцях. Іншими словами, якщо вхідна база 10 FFне представляла б 255, вона представляла б (15 * 10) + 15або 165.

Насправді це працює на всі цифри 0до Fбудь-якої вхідної бази 2до 16. Тож якщо вхідна база дорівнює 5, то 26Eбуде (2 * 5^2) + (6 * 5) + 14, або 94.

Зауважте, така поведінка діє для немодифікованих джерел GNU. Однак, як зазначає @SophiaLechner, дистрибутори, засновані на RedHat, використовують bc-1,06-dc_ibase.patch, який змінює цю поведінку, тому цифри> = ibase трактуються як ibase - 1незалежно від їх фактичного значення. Зауважте, TIO, dc схоже, не має bc-1,06-dc_ibase.patch (навіть якщо його Fedora 28 ¯_ (ツ) _ / ¯).


Це не зовсім правильно - хоча одиничні цифри над вхідною базою інтерпретуються так, як ви сподіваєтесь, якщо літерал має кілька цифр або навіть десяткову точку, недійсні цифри для бази трактуються як (base-1). Отже, вхідна база 10 FFявляє собою 99, у вхідній базі 5 26Eте саме, що є 244, тобто база 10 74.
Софія Лехнер

@SophiaLechner Ви впевнені? tio.run/##S0n@/9/QIJ/L0CCTy82tgMs0k8vIzLXg/38A Яку dcверсію ти працюєш? Я використовую GNU dc 1.4.1 для ubuntu та GNU dc 1.3 на MacOS
Digital Trauma

Цікаво. Я запускаю 1.3.95 на Red Hat, і ось ваша прикладна програма: [slechner @ XXX] $ dc -e '10o 10i FFp 5i 26Ep' 99 74 [slechner @ XXX] $ dc - перетворення DC (GNU bc 1,06 .95) 1.3.95
Софія Лехнер

Argh ... не можна змусити блокувати код працювати в коментарі. Справа в тому, що FFpвиходи 99в 1,3.95. Чи можете ви це перевірити у версії MacOS?
Софія Лехнер

1
Ясна річ! Дякую за всі дослідження.
Софія Лехнер

2

При ініціалізації макроса функції (ми будемо використовувати F), який потрібно запустити негайно, використовуйте щось на кшталт, dsFxа не sFlFx. Те саме працює для змінних: dsaа не sala.

Якщо вам потрібно виконати інші речі між зберіганням та завантаженням (наприклад, sa[other stuff]la), все-таки подумайте, чи є вищезгаданим життєздатним: Якщо ви залишите значення на стеку перед іншими операціями, чи воно буде повернене вгорі до кінця цих операцій?


2

Щойно це відкрили випадково. Ще один спосіб створення нуль: _.

_є сигналом постійного струму, що наступні цифри - це від’ємне число. Приклад:

_3 # pushes -3

Але що робити, якщо ми не будемо слідувати за цим номером?

_ # pushes 0...sometimes

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

c4 5_6  # -6,5,4
c4 5_ 6 # -6,5,4
c4 5_
6       # -6,5,4 # still a negative sign since the next thing it sees is a digit
c4 5_z  #  3,0,5,4 # if it's followed by a non-digit, it's a 0
c4 5_p6 #  6,0,5,4
c4 _*   #  0 # 4*0=0

1

Якщо вміст усього стека потребує друку в кінці програми, для цього може бути використаний рекурсивний цикл макросів. Однак набагато коротше просто використовувати fкоманду.


1

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

Наприклад seq 10 | dc -e'?f', seqвиводить цілі числа 1-10, по одному на рядок. ?буде тільки читати перший , 1який буде виводитися , коли fскидає весь стек. Однак у seq 10 | tr '\n' ' ' | dc -e'?f', trцілі вхідні дані роблять весь простір відокремленим. У цьому випадку за один раз ?прочитає всі цілі числа з рядка та fвиведе їх усі.


1

Якщо оператор обмежений у джерелі, зробіть новий з допомогою a

Щось мені зараз було в нагоді - це уникати використання конкретного оператора, натискаючи значення ASCII оператора, використовуючи його aдля перетворення в рядок, і sзапускає це в реєстр, який буде виконано як макрос пізніше на. Наприклад, мені потрібно зробити поділ, але мені заборонено або намагаються уникати використання символу /. Я можу замість цього зробити, 47asdа потім, коли мені потрібно розділити 16 на 4,16 4 ldx ,.

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

1

Уникаючи пробілів

Уникнути пробілів виникає досить багато проблем, і, як правило, легко dc. Крім рядків, то один дуже певний час , що пробільні стає необхідним, коли штовхаючи кілька номерів поспіль: 1 2 3. Якщо цього потрібно уникати:

  • Виконати порожній макрос між: 1[]x2[]x3[]x.
  • Якщо дужки на столі, зберігати NOP макроскопически заздалегідь: 35asnі виконати його між ними: 1lnx2lnx3lnx.

Ви також можете робити коми через окремі номери, якщо ви готові помиритися з dc: ',' (054) unimplementedпопередженнями.
Цифрова травма

Я не думав про це - мабуть, це стосується будь-якого даного маркера, який не вирішує команди… цікаво…
brhfl
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.