Нечитабельна , 2199 2145 2134 2104 2087 2084 байт
Підтримує як k
/, j
так і ▲
/ ▼
синтаксис.
За доброю нечитабельною традицією, ось програма, відформатована пропорційним шрифтом, щоб придушити різницю між апострофами та подвійними лапками:
"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" ""
Це було дивовижною проблемою. Дякую за публікацію!
Пояснення
Щоб відчути, що не можна прочитати, а що не можна зробити, уявіть Brainfuck з нескінченною стрічкою в обох напрямках, але замість того, щоб вказівник пам’яті переміщав одну клітинку за часом, ви можете отримати доступ до будь-якої комірки пам’яті, відкинувши покажчик. Це дуже зручно в цьому рішенні, хоча інші арифметичні операції, включаючи модуль, потрібно виконувати вручну.
Ось програма як псевдокод з коментарем режисера:
// Initialize memory pointer. Why 5 will be explained at the very end!
ptr = 5
// FIRST PASS:
// Read all characters from stdin, store them in memory, and also keep track of the
// current line number at each character.
// We need the +1 here so that EOF, which is -1, ends the loop. We increment ptr by 2
// because we use two memory cells for each input character: one contains the actual
// character (which we store here); the other will contain the line number at which the
// character occurs (updated at the end of this loop body).
while ch = (*(ptr += 2) = read) + 1:
// At this point, ch will be one more than the actual value.
// However, the most code-economical way for the following loop is to
// decrement inside the while condition. This way we get one fewer
// iteration than the value of ch. Thus, the +1 comes in handy.
// We are now going to calculate modulo 4 and 5. Why? Because
// the mod 4 and 5 values of the desired input characters are:
//
// ch %5 %4
// ^ 1
// v 2
// k 3
// j 4
// ▲ 0 2
// ▼ 0 0
//
// As you can see, %5 allows us to differentiate all of them except ▲/▼,
// so we use %4 to differentiate between those two.
mod4 = 0 // read Update 2 to find out why mod5 = 0 is missing
while --ch:
mod5 = mod5 ? mod5 + 1 : -4
mod4 = mod4 ? mod4 + 1 : -3
// At the end of this loop, the value of mod5 is ch % 5, except that it
// uses negative numbers: -4 instead of 1, -3 instead of 2, etc. up to 0.
// Similarly, mod4 is ch % 4 with negative numbers.
// How many lines do we need to go up or down?
// We deliberately store a value 1 higher here, which serves two purposes.
// One, as already stated, while loops are shorter in code if the decrement
// happens inside the while condition. Secondly, the number 1 ('""") is
// much shorter than 0 ('""""""""'""").
up = (mod5 ? mod5+1 ? mod5+3 ? 1 : 3 : 2 : mod4 ? 3 : 1)
dn = (mod5 ? mod5+2 ? mod5+4 ? 1 : 3 : 2 : mod4 ? 1 : 3)
// As an aside, here’s the reason I made the modulos negative. The -1 instruction
// is much longer than the +1 instruction. In the above while loop, we only have
// two negative numbers (-3 and -4). If they were positive, then the conditions in
// the above ternaries, such as mod5+3, would have to be mod5-3 etc. instead. There
// are many more of those, so the code would be longer.
// Update the line numbers. The variables updated here are:
// curLine = current line number (initially 0)
// minLine = smallest linenum so far, relative to curLine (always non-positive)
// maxLine = highest linenum so far, relative to curLine (always non-negative)
// This way, we will know the vertical extent of our foray at the end.
while --up:
curLine--
minLine ? minLine++ : no-op
maxLine++
while --dn:
curLine++
minLine--
maxLine ? maxLine-- : no-op
// Store the current line number in memory, but +1 (for a later while loop)
*(ptr + 1) = curLine + 1
// At the end of this, minLine and maxLine are still relative to curLine.
// The real minimum line number is curLine + minLine.
// The real maximum line number is curLine + maxLine.
// The total number of lines to output is maxLine - minLine.
// Calculate the number of lines (into maxLine) and the real minimum
// line number (into curLine) in a single loop. Note that maxLine is
// now off by 1 because it started at 0 and thus the very line in which
// everything began was never counted.
while (++minLine) - 1:
curLine--
maxLine++
// Make all the row numbers in memory positive by adding curLine to all of them.
while (++curLine) - 1:
ptr2 = ptr + 1
while (ptr2 -= 2) - 2: // Why -2? Read until end!
*ptr2++
// Finally, output line by line. At each line, we go through the memory, output the
// characters whose the line number is 0, and decrement that line number. This way,
// characters “come into view” in each line by passing across the line number 0.
while (--maxLine) + 2: // +2 because maxLine is off by 1
ptr3 = 5
while (ptr -= 2) - 5:
print (*((ptr3 += 2) + 1) = *(ptr3 + 1) - 1) ? 32 : *ptr3 // 32 = space
ptr = ptr3 + 2
print 10 // newline
Стільки для логіки програми. Тепер нам потрібно перекласти це на "Нечитабельно" і скористатися ще кількома цікавими трюками з гольфу.
Змінні завжди відмічаються чисельно в Нечитабельному (наприклад, a = 1
стає чимось подібним *(1) = 1
). Деякі числові літерали довші за інші; найкоротший - 1, за ним - 2 і т. д. Щоб показати, скільки довші від’ємні числа, ось цифри від -1 до 7:
-1 '""""""""'""""""""'""" 22
0 '""""""""'""" 13
1 '""" 4
2 '""'""" 7
3 '""'""'""" 10
4 '""'""'""'""" 13
5 '""'""'""'""'""" 16
6 '""'""'""'""'""'""" 19
7 '""'""'""'""'""'""'""" 22
Зрозуміло, ми хочемо віднести змінну №1 до тієї, яка найчастіше зустрічається в коді. У першому циклі while це, безумовно mod5
, що з’являється в 10 разів. Але нам більше не потрібно mod5
після першого циклу while, тому ми можемо переділити те саме місце пам'яті іншим змінним, які ми використовуємо згодом. Це є ptr2
і ptr3
. Зараз на цю зміну посилається 21 раз. (Якщо ви намагаєтесь порахувати кількість подій самостійно, не забудьте порахувати щось на зразок a++
двічі, один раз для отримання значення та один раз для його встановлення.)
Є лише одна змінна, яку ми можемо повторно використовувати; після того як ми обчислили значення модуля, ch
це більше не потрібно. up
і dn
придумуйте однакову кількість разів, тому або добре. Давайте об’єднаємось ch
із up
.
Це залишає загалом 8 унікальних змінних. Ми можемо виділити змінні від 0 до 7, а потім запустити блок пам'яті (що містить символи та номери рядків) з 8. Але! Оскільки 7 є такою ж довжиною в коді, що і -1, ми можемо також використовувати змінні від 1 до 6 і запустити блок пам'яті з 7. Таким чином, кожне посилання на початкове положення блоку пам'яті у коді трохи коротше! Це залишає нам такі завдання:
-1 dn
0 ← ptr or minLine?
1 mod5, ptr2, ptr3
2 curLine
3 maxLine
4 ← ptr or minLine?
5 ch, up
6 mod4
7... [data block]
Тепер це пояснює ініціалізацію в самому верху: це 5, тому що це 7 (початок блоку пам'яті) мінус 2 (обов'язковий приріст у першій умові). Те саме стосується двох інших випадків 5 в останній циклі.
Зауважте, оскільки 0 і 4 однакові за довжиною в коді, ptr
і їх minLine
можна розподілити в будь-якому напрямку. ... Або вони могли?
Що з таємничим 2 у другому останньому циклі while? Чи не повинно це бути 6? Ми хочемо лише зменшити числа в блоці даних, правда? Як тільки ми досягнемо 6, ми знаходимося поза блоком даних, і нам слід зупинитися! Це було б вразливістю помилки помилки переповнення буфера!
Ну, подумайте, що станеться, якщо ми не зупинимось. Зменшуємо змінні 6 і 4. Змінна 6 є mod4
. Це використовується тільки в першому циклі while і більше не потрібне тут, так що ніякої шкоди не було зроблено. Як щодо змінної 4? Як ви думаєте, ptr
чи має бути змінною 4 чи має бути minLine
? Правильно, minLine
більше не використовується в даний момент! Таким чином, змінна №4 є, minLine
і ми можемо сміливо її зменшити і не завдати шкоди!
ОНОВЛЕННЯ 1! Golfed від 2199 до 2145 байт, розуміючи , що dn
може також бути об'єднаний з mod5
, хоча mod5
до цих пір використовується в обчисленні значення для dn
! Нове призначення змінної зараз:
0 ptr
1 mod5, dn, ptr2, ptr3
2 curLine
3 maxLine
4 minLine
5 ch, up
6 mod4
7... [data block]
ОНОВЛЕННЯ 2! Поле з 2145 по 2134 байт, зрозумівши, що, оскільки mod5
зараз він знаходиться в тій же змінній dn
, що і нараховується до 0 за цикл, mod5
більше не потрібно явно ініціалізувати до 0.
ОНОВЛЕННЯ 3! Гольфував з 2134 по 2104 байт, реалізуючи дві речі. По- перше, хоча ідея «негативний по модулю» варто його mod5
, то ж міркування не відноситься до , mod4
тому що ми ніколи не випробування проти і mod4+2
т.д. Таким чином, зміна mod4 ? mod4+1 : -3
до mod4 ? mod4-1 : 3
призводить нас до 2110 байт. По-друге, оскільки mod4
це завжди 0 або 2, ми можемо ініціалізувати mod4
на 2 замість 0 і повернути два тернарі ( mod4 ? 3 : 1
замість mod4 ? 1 : 3
).
ОНОВЛЕННЯ 4! Поле з 2104 по 2087 байт, розуміючи, що цикл while, який обчислює значення модуля, завжди працює хоча б один раз, і в такому випадку Unreadable дозволяє повторно використовувати значення останнього твердження в іншому виразі. Таким чином, замість того, що while --ch: [...]; up = (mod5 ? mod5+1 ? [...]
ми маємо зараз up = ((while --ch: [...]) ? mod5+1 ? [...]
(і всередині цього циклу while, ми обчислюємо mod4
перше, так що mod5
це останнє твердження).
ОНОВЛЕННЯ 5! Гольфував з 2087 по 2084 байт, зрозумівши, що замість того, щоб виписати константи 32
і 10
(пробіл та новий рядок), я можу зберігати число 10 у (зараз не використовується) змінній №2 (назвемо це ten
). Замість того, щоб ptr3 = 5
ми писати ten = (ptr3 = 5) + 5
, тоді 32
стає ten+22
і print 10
стає print ten
.