Це було цікавим викликом працювати над, різдвяними святкуваннями, умикатися та вимикатись. Дякую за публікацію! Гольф це було цікаво, тому що специфікація повна винятків та особливих випадків, для яких потрібні великі умови. Крім того , в той час як я не потрібно конвертувати і з десятковою маються на в цей час я так потрібен «Макс» функцію свого роду , щоб визначити найбільшу кількість цифр в кожному номері і найбільше значення цифр в кожному місці.
Перша версія цього становила 4844 байтів, просто для того, щоб дати вам уявлення про те, наскільки я це гольфу.
Програма очікує введення як розділений комою список цілих чисел. Ні пробілів, ні нових рядків. Використання цих даних спричинить невизначеність поведінки.
"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
Пояснення
Я збираюся ознайомити вас із тим, як працює програма, показуючи, як вона обробляє конкретний вхід 202,100,1
.
На початку ми побудуємо кілька значень, які нам знадобляться пізніше - переважно коди ASCII символів, які ми виведемо.
Як бачите, '8'
і '.'
вже доступні. '|'
однак це дійсно 124, а не 14. Ми використовуємо цикл while, щоб додати до нього двічі тимчасове значення у слоті №1, щоб отримати 124 (що становить 14 + 55 × 2, оскільки цикл у той час як працює для 56−1 = 55 ітерації). Це економить кілька байтів, оскільки великі цілі букви, такі як 124, дійсно довгі. На наступній схемі я показую розташування кожної змінної, яку використовує програма.
Далі ми хочемо ввести всі символи та зберегти їх на стрічці, починаючи з комірки №12 ( p - це запущений покажчик для цього). У той же час ми хочемо знати, наскільки довге найдовше число (скільки цифр). Щоб досягти цього, ми зберігаємо пробіг унітарного руху вліво, починаючи з комірки № −1 (ми використовуємо q як запущений покажчик). Після першого вхідного номера ( 202
) стрічка тепер виглядає так:
Ви помітили, що числа вимкнено на 4. Ну, коли ми вперше вводимо їх, це їх значення ASCII, тому вони "вимкнено" на 48, а кома - 44. Для кожного символу ми копіюємо 46 з '.'
в r, а потім віднімаємо його циклом while (який віднімає 45), а потім додаємо 1. Робимо так, щоб кома (наш роздільник) дорівнювала 0, тому ми можемо використовувати умовне розпізнавання.
Також ви помітили, що ми залишаємо комірку №11 в 0. Нам це потрібно, щоб розпізнати межу першого числа.
Наступним символом буде кома, тому ми зберігаємо 0 в №15, але, звичайно, цього разу ми не просуваємо q . Замість цього ми повертаємо q назад до 0 і починаємо «перезаписувати» 1, які ми вже розмістили.
Після обробки всіх решти символів ми отримуємо це:
Як бачимо, значення 1s, написані q, тепер вказують (унарні) довжину найдовшого числа.
Зараз ми використовуємо цикл час для переміщення q вліво, а потім розміщуємо там інший покажчик, який я буду називати r2 . Призначення r2 стане зрозумілим пізніше.
На цьому етапі дозвольте мені уточнити термінологію, якою я буду користуватися протягом усього цього.
- Під числом я маю на увазі одне з вхідних чисел, які розділені комами. У нашому прикладі їх 202, 100 та 1.
- Під цифрою я маю на увазі одну цифру в конкретному одному з чисел. Перше число має 3 цифри.
- Під місцем я маю на увазі ті місця, десятки місце, сотні місця і т. Д. Тож якщо я скажу «цифри в поточному місці», а поточне місце - це те місце, ці цифри складають 2, 0 і 1 в цьому замовлення.
Тепер повернемося до нашого звичайного програмування. Весь решта програми - це великий цикл, який рухається q вперед, поки не досягне клітинки № 0. Кожна комірка по дорозі являє собою місце, причому ті розташовуються в крайній правій частині, а q почнеться з найбільш значущої. У нашому прикладі це сотні.
Продовжуємо, збільшуючи точки комірки q на (тобто * q ).
Зараз ми перебуваємо на "етапі 2" для сотні. На цьому етапі ми з’ясуємо, яка найбільша цифра серед усіх цифр у сотнях. Для цього ми використовуємо той самий одинарний фокус підрахунку, за винятком цього разу вказівник називається r і покажчик r2 позначає його вихідне положення, до якого нам потрібно скидати його щоразу, коли ми переходимо до наступного числа.
Почнемо з першого числа. Почнемо, встановивши p до 11 (твердо закодоване вихідне положення всіх чисел). Потім ми використовуємо цикл час, щоб знайти кінець числа і встановлюємо там p2 для позначення позиції. Одночасно ми також встановлюємо q2 до 0:
Не відволікайтеся на те, що q2 вказує на vars. У нас немає пробивки порожньої комірки, тому що ми можемо виявити клітинку № 0 просто тому, що це число нуль.
Далі ми проходимо через поточне число, зменшуючи p і q2 разом, поки * p не дорівнює нулю. У кожному місці значення * q2 вказує нам, що нам потрібно зробити. 1 означає "нічого не робити", тому ми продовжуємо йти. Врешті-решт ми стикаємось із двома у комірці №3. Кожен раз * q2 не дорівнює 1, q2 завжди дорівнює q .
Як я вже говорив, етап 2 - це "визначити найбільшу цифру в цьому місці". Таким чином, ми встановлюємо r на r2 , використовуємо цикл час для зменшення * p і переміщаємо r вліво і заповнюємо стрічку 1s, а потім використовуємо інший цикл while, щоб повернути r назад вправо, а інкремент * p знову, щоб відновити значення. Пам’ятайте, що кожен цикл у той час як цикл працює на одну меншу кількість ітерацій, ніж значення, яке ми використовуємо; через це кількість записаних 1с буде на 3 більше (а не на 4 більше), ніж значення цифри, а остаточне значення, збережене в * p, буде ще на 2. Таким чином, це ефективно зменшило * p на 2.
Після цього встановлюємо p на значення p2, а потім робимо все це знову. Вдруге встановіть q2 до 0, знайдіть кінець числа, перемістивши p праворуч, а потім пройдіть цифри цього числа, декрементуючи p і q2 разом. Ще раз ми зустрінемо 2 у комірці №3 і запишемо, що залишилось багато 1s від * r .
У випадку третього числа ми нічого не робимо, тому що у нього немає сотень місця (тому q2 ніколи не досягає q ), але це нормально, оскільки це не впливає на обчислення максимального значення цифри.
Також ми встановлюємо комірку * (r - 4) , яку я позначив тут без маркованої стрілки, до 1 (хоча вона вже на 1). Я не збираюся розповідати, чому ще, але, можливо, ви вже здогадалися?
Наступний приріст * q приведе нас до етапу 3, який "віднімає максимальну цифру від усіх цифр у поточному місці". Як і раніше, ми скидаємо p до 11 і q2 до 0, а потім проходимо всі числа так само, як ми це робили на попередньому етапі; за винятком цього часу, * q = 3 замість 2. Кожного разу, коли q2 відповідає q і p знаходиться в сотнях, ми використовуємо цикл час для зменшення * p стільки разів, скільки в блоці зліва * r2 є 1s (5 у нашому прикладі), використовуючи rяк біг вказівник. Ми фактично декрементуємо це ще раз, щоб найбільша цифра закінчилася на −2, з причини, яка стане зрозумілою пізніше:
Після того як ми обробили всі числа, ми зараз на кінці етапу 3. Тут ми виконуємо дві особливі речі.
- Спочатку ми також віднімаємо розмір r -block (плюс 1) від * q , але використовуючи вказівник r2 , який залишає його зліва. * q стає таким негативним. У нашому випадку r -блок має п'ять 1s, тому * q стає −3.
- По- друге, ми встановлюємо змінну з в нульове значення , щоб вказати , що ми зараз вступаємо вихідний каскад. (Технічно факт, що * q є негативним, вже вказує на вихідний етап, але це занадто складно перевірити, отже, додаткова змінна.)
Тепер ви розумієте, що ми продовжуємо перебирати числа, знаходимо поточне місце (вказане не-1 значенням * q ) у кожному номері, і робимо щось залежно від значення * q . Ми бачимо, що * q спочатку збільшується до 2 (= обчислює максимальне значення цифри), потім 3 (віднімає максимальне значення цифри з кожної цифри в цьому місці), а потім віднімаємо з нього, щоб зробити його від’ємним. Звідти воно продовжуватиметься вгору, поки не досягне 1, тим самим відновивши значення, що означає "нічого не робити". У цей момент ми переходимо до наступного місця.
Тепер, коли * q негативний, ми виводимо. * q - це саме правильне значення, щоб ми вивели потрібну кількість рядків символів до того, як воно досягне 1; якщо найбільша цифра дорівнює 2, нам потрібно вивести 3 ряди. Подивимося, що відбувається при кожному значенні * q :
- * q = −2:
- Для першого числа * p - −2, що означає, що нам потрібно вивести a
'.'
(крапка) або a ':'
(двокрапка). Ми вирішуємо, що, дивлячись на q : якщо це −1, ми знаходимося в тому місці, тому виводимо a ':'
(який обчислюємо як '8'
+2), інакше a '.'
.
- Для другого числа * p дорівнює −3. Все, що не є −2, означає, що ми виводимо
'|'
(трубу), а потім збільшуємо значення. Таким чином він досягне -2 в потрібному місці, і тоді ми виведемо '.'
s / ':'
s для решти цієї цифри.
- У кожному випадку ми також встановлюємо змінну pd до 0, перш ніж обробити число, і встановимо pd (= "надрукований") на ненульове значення, щоб вказати, що ми надрукували символ.
- Для третього числа жодна обробка не відбувається, оскільки третя цифра не має сотні місць. У цьому випадку pd все одно буде 0 після обробки числа, що вказує на те, що нам все-таки потрібно вивести a
'|'
(але лише якщо out є не нульовим, оскільки в іншому випадку ми все ще перебуваємо на етапі 2 або 3).
- Після обробки всіх чисел, якщо з не дорівнює нуль, вихід нового рядка. Зауважте, що нам потрібна змінна зміна, щоб ми не виводили новий рядок на етапі 2 або 3.
- * q = −1: Те саме, що і раніше, за винятком того, що * p дорівнює 2 для обох перших двох чисел, тому обидва виводять a
'.'
(і третій виводить a,'|'
як і раніше).
- * q = 0: Коли * q дорівнює 0, це означає «нічого не робити, якщо ми на одному місці, інакше виведіть рядок
'|'
s незалежно від * p ». Таким чином ми отримуємо прокладку між цифрами.
Тепер ми збільшуємо q, щоб перейти до наступного місця, десятків місця та приросту * q там. На початку 2-го етапу стрічка виглядає так:
Потім ми виконуємо Етап 2 так само, як і раніше. Пам'ятайте, що це ефективно віднімає 2 з кожної цифри в цьому місці, а також залишає одинарне число, що залишилося від * r2, що вказує максимальну цифру. Ми залишаємо попередній одинарний номер у спокої і просто продовжуємо продовжувати стрічку вліво; тільки "коштувати" зайвий додатковий код. Коли ми закінчимо, і ми наростимо * q , на початку Етапу 3 стрічка тепер:
Власне, це брехня. Згадайте раніше, де я казав, що ми встановили * (r - 4) на 1, і я не сказав вам, чому? Зараз я вам скажу, чому. Це для таких випадків, коли найбільша цифра насправді дорівнює 0, тобто всі цифри в цьому місці дорівнюють 0. Установка * (r - 4) , позначена стрічкою, що не позначається вище, на 1 розширює одинарне число на 1, але лише в цьому окремому випадку. Таким чином ми робимо вигляд, ніби найбільша цифра була 1, а значить, ми виведемо один додатковий рядок.
Після етапу 3 (віднімайте максимальну цифру від усіх цифр у поточному місці), включаючи додатковий крок, який робить * q негативним, стрічка виглядає так. Минулого разу найбільша цифра була представлена −2 у блоці * p , але цього разу вони всі −3, оскільки всі вони насправді нулі, але ми робимо вигляд, ніби максимальна цифра була 1.
Тепер давайте подивимося, що відбувається, коли * q просувається до 1:
- Коли * q = −1, значення p мають усі −3, а значить, ми виводимо
'|'
s та збільшуємо їх.
- Коли * q = 0, ми виводимо,
'|'
тому що це ми завжди робимо, коли * q = 0, незалежно від * p .
Таким чином, отримуємо два ряди труб.
Нарешті, ми переміщуємо * q на своє місце. Це стає цікавим, тому що нам потрібно вивести ':'
s, якщо фактична цифра не що інше, а 1, а '8'
якщо це 1. Давайте подивимось, як триває програма. По-перше, ми збільшуємо * q, щоб ініціювати Етап 2:
Після Етапу 2 ("обчислити максимальне значення цифри") нам залишиться наступне:
Після етапу 3 ("віднімайте максимальне значення від усіх цифр у поточному місці") стрічка виглядає так:
Тепер по черзі пройдемо кожну ітерацію * q :
- * q = −2:
- Перше число: вже при −2, тому виведіть a
':'
(а не a, '.'
тому що q = −1).
- Друге число: при −4, тому виведіть a
'|'
та приріст.
- Третє число: при −3, тому виведіть a
'|'
. Однак цього разу, замість збільшення, спрацьовує особливий випадок. Тільки якщо ми виводимо останнє місце ( q = −1), і ми знаходимося в другому останньому рядку для цього ( * q = −2), і цифра насправді є 1 ( * p = −3) , то замість того, щоб збільшити його до −2, встановимо його до −1. Іншими словами, ми використовуємо -1 як особливе значення, щоб вказати, що в наступній ітерації нам потрібно буде вивести '8'
замість ':'
.
- * q = −1:
- Перше число: вже на −2, тому виведіть a
':'
.
- Друге число: при −3, тому виведіть a
'|'
. Особлива умова не спрацьовує, оскільки * q вже не −2. Тому прирощення.
- Третє число: при −1, тому виведіть
'8'
.
- * q = 0: Зазвичай ми виводимо тут рядок прокладки
'|'
s, але в спеціальному випадку, коли ми знаходимося в одному місці ( q = −1), ми пропускаємо це.
Після цього q збільшується до 0, а велика в той час як цикл закінчується.
Тепер ви знаєте, як працює такий вхід 202,100,1
. Однак є ще один особливий випадок, який ми досі не висвітлювали. Ви можете пам'ятати, що, коли ми обробляли останнє місце, коли * p був −3, ми встановлювали його на −1 для 1
(замість того, щоб збільшувати його до −2), щоб наступна ітерація виводила '8'
замість цього. Це працює лише тому, що у нас є ітерація, в якій * p дорівнює −3, і ми приймаємо рішення щодо збільшення її чи встановлення до −1. У нас немає такої ітерації, якщо всі цифри в одному місці є 0 або 1. У такому випадку всі значення * p для 1s починаються з −2; немає можливості вирішити встановити його на -1а не збільшувати його від −3 . Через це в Етапі 3 є ще одна спеціальна обсадна оболонка («відняти максимальну цифру від кожної цифри на поточному місці»). Я стверджував, що після вирахування максимального значення цифри з кожної цифри (в якій момент максимальна цифра знаходиться на −1) ми просто ще раз зменшимо її, але насправді існує умова, яка полягає в наступному:
Якщо цифра, яку ми дивимось, дорівнює максимальній цифрі в цьому місці ( * p = −1), і це місце є тим, що розміщується ( q = −1), а максимальна цифра дорівнює 1 ( * (r + 5) = 0, тобто одинарний блок зліва зліва становить лише 5 комірок), тільки тоді залишаємо * p на −1, щоб вказати, що для єдиної ітерації виходу повинно виходити an '8'
. У всіх інших випадках ми декрементуємо його ще раз.
Зроблено. Щасливого Нового року!
Редагувати 1 (3183 → 3001): кілька новорічних гольфів! Мені вдалося повністю позбутися змінних p2 та r2 ! p тепер перегони вперед і назад, щоб продовжувати знаходити початок і кінець чисел, але, схоже, це коротше в коді. Я також намагався позбутися q2 , але не зміг таким чином скоротити код.
Я також знайшов ще кілька місць, де я міг застосувати типові нечитані трюки з гольфу, як повторне використання останнього значення циклу. Навести вам приклад замість
while *(++p) { 1 } // just increment p until *p is 0; the 1 is a noop
if (pd) { x } else { y } // where pd is a variable
Я можу зберегти '""""
(зробити перший, потім другий) і '"""
(константа 1), записавши його таким чином, як
if (while *(++p) { pd }) { x } else { y }
Звичайно, це працює лише в тому випадку, якщо я знаю, що цикл while буде виконуватись принаймні однією ітерацією, але якщо він є, його повернене значення - pd, і я можу використовувати це як умову для if.