Класно бути програмістом на C++: захотів покодити іншою мовою — скомпілив її 😂
От я сьогодні з цього почав, бо вирішив згадати мову io, але під arm64 на macOS її немає, бо там якийсь асемблерний код не компілюється. Автори кажуть, мовляв, треба окрему версію Homebrew поставити для цього, але то якогось занадто головняково, тож я просто скомпілював з сорців. Версія з
Першу сьогоднішню задачу з Advent of Code я розвʼязав дуже швидко, а от другу вже повертів туди-сюди, бо, як це часто буває, налажав десь у корнер-кейсах. А там якраз був ОДИН такий😅
Тепер про саму мову. Вона наче й не складна, але є там трохи дивні моменти, коли начебто схожі конструкції поводять себе трохи по-різному.
З класного: там все є виразом (expression), тобто у всього є результат, тому можна хоч цикл
Ще з цікавого: вирішив я написати функціюз трохи тупою назвою
Використовую її потім якось отак (тут
Прикол тут наступний: якогось спеціального синтаксису для лямбд немає.
У
Працює io десь разів у 10 повільніше за Haskell. Файл на 1000 рядків у мене оброблявся 200 з копійками мілісекунд. З іншого боку, оптимізаціями там і не пахне. Не казатиму вам, яка складність у мого алгоритму, бо там треш ))
От я сьогодні з цього почав, бо вирішив згадати мову io, але під arm64 на macOS її немає, бо там якийсь асемблерний код не компілюється. Автори кажуть, мовляв, треба окрему версію Homebrew поставити для цього, але то якогось занадто головняково, тож я просто скомпілював з сорців. Версія з
master
одразу падала з помилкою, тому довелося трохи пострибати по теґах.Першу сьогоднішню задачу з Advent of Code я розвʼязав дуже швидко, а от другу вже повертів туди-сюди, бо, як це часто буває, налажав десь у корнер-кейсах. А там якраз був ОДИН такий
Тепер про саму мову. Вона наче й не складна, але є там трохи дивні моменти, коли начебто схожі конструкції поводять себе трохи по-різному.
З класного: там все є виразом (expression), тобто у всього є результат, тому можна хоч цикл
for
записати у змінну. Ну, насправді це й не цикл, а просто функція в Object
. Взагалі треба дуже уважно дивитися, що яка функція повертає, бо інколи це не те що ти очікуєш.Ще з цікавого: вирішив я написати функцію
mapPairs
, яка виконує блок коду на кожній парі зі списку та повертає список результатів. Мабуть, можна було цікавіше щось вигадати, але пох.Використовую її потім якось отак (тут
report
— це список):increasing := true
decreasing := true
report mapPairs(l, r,
increasing = increasing and l < r
decreasing = decreasing and l > r
result := (increasing or decreasing) and (l - r) abs <= 3
"#{l}, #{r}: #{result}" interpolate println
result
)
Прикол тут наступний: якогось спеціального синтаксису для лямбд немає.
mapPairs
— це просто метод з трьома параметрами: l
, r
та блок коду. Причому l
та r
я до того ніде не створював, але в блоці коду їх використовую разом з іншими змінними з локального скоупа, звідки викликаю це. Як же це працює? А отак:List mapPairs := method(
left := call argAt(0) name
right := call argAt(1) name
body := call argAt(2)
context := Object clone prependProto(call sender)
if(call sender hasLocalSlot("self"),
context setSlot("self", call sender self)
)
result := List clone
for (i, 0, self size - 2,
context setSlot(left, self at(i))
context setSlot(right, self at(i + 1))
result append(context doMessage(body))
)
result
)
У
context
клонується все з sender
(так-так, в io методи — це надсилання повідомлень, як у SmallTalk), потім у ньому ж створюються l
та r
, а потім отой блок коду виконується вже в цьому контексті. Трошки винесло мені мозок короч.Працює io десь разів у 10 повільніше за Haskell. Файл на 1000 рядків у мене оброблявся 200 з копійками мілісекунд. З іншого боку, оптимізаціями там і не пахне. Не казатиму вам, яка складність у мого алгоритму, бо там треш ))
Please open Telegram to view this post
VIEW IN TELEGRAM
Так-так, задовбав уже вас усіх з Advent of Code. Але довго я не витримаю, обіцяю.
Втім сьогодні задачі були вкрай легкі. І особливо — якщо розвʼязувати їх за допомогою мови🆕 .
Вкотре нагадую, що Nushell — це наразі найкрутіша та найзручніша оболонка, яка до того ж ще й стрімко розвивається (і написана на Rustas if someone cares ). Мова Nu — статично типізована, передає через пайп структуровані дані, має купу корисних функцій у стандартній бібліотеці тощо. Просто погляньте:
Кайф же! (Взагалі-то тут можна було одним
Забудьте про Fish, PowerShell чи, боже збав, Zsh та Bash — тільки Nushell!
Втім сьогодні задачі були вкрай легкі. І особливо — якщо розвʼязувати їх за допомогою мови
Вкотре нагадую, що Nushell — це наразі найкрутіша та найзручніша оболонка, яка до того ж ще й стрімко розвивається (і написана на Rust
export def solve-one [fileName: string] [nothing -> int] {
log info $"Reading from ($fileName)"
let result = (open $fileName
| parse --regex '(mul\((?P<a>\d+),(?P<b>\d+)\))'
| into int a b
| upsert c {|it| $it.a * $it.b}
| math sum
| get c
)
$result
}
Кайф же! (Взагалі-то тут можна було одним
reduce
все зробити, але мені отак покроково більше подобається). І працює швидко: приклади та вхідні дані для обох задач обробляє за 20 мс разом узяті. Забудьте про Fish, PowerShell чи, боже збав, Zsh та Bash — тільки Nushell!
Please open Telegram to view this post
VIEW IN TELEGRAM
Останнім часом дуже пізно прокидаюся, тож в AoC-лідербордах (навіть у моєму власному 😺 🤮 . А потім дивлюся на годинник — майже сьома 🕖
Короч попри те, що поспав менше чотирьох годин, не втримався та сів глянути, що за задачки. Писав на Swift🕊 — і це мій перший досвід. Мова сама по собі непогана, хоча й не сказати, що я побачив там якісь прям дуже свіжі ідеї — радше це доволі приємна комбінація вже існуючих. Синтаксисом чогось місцями дуже нагадала C#, тільки без зайвих символів типу крапки з комою та дужок, де не треба. Є опціональні типи (aka монада Maybe), які вбудовані прямо в мову, є класи, структури, енами, актори, є зіставлення з шаблоном тощо. Ну короч доволі приємна для написання мова.
Вибрав її, бо хотів згодом спробувати нею під мікроконтролери щось написати: від сішки ригаю, C++ замахав — хочеться вже щось для душі, тільки б не Python.
Що не сподобалося — повідомлення про помилки. Ну тобто круто, що компілятор багато всього перевіряє, а потім тобі ще й пише, от тільки пише він забагато хуйні. Каже мені, мовляв, не можу викликати функцію з такими параметрами, бо там типи отакі, а воно очікує інші. А потім ще простирадло тексту про те, як він намагався інші варіанти функції підібрати, але нічого не спрацювало — прям як вивід про помилки з шаблонами в C++, їй-богу. Або ще дивіться:
Кричить на мене прям страшними словами. Виявляється, що не вистачало
За годину часу, звісно, я повністю мову не відчув. Треба погратися з корутинами хоча б та подивитися, що там ще є. Але задачки для Advent of Code я розвʼязав, а в кінці ще й встиг на стрім пана Соловйова, де він бився з маніпуляціями зі списками в кложі😆 Я у себе в першій задачі просто ту матрицю потранспоузив туди-сюди, «згенерив блядські діагоналі» © — та й потому. Хоча мушу визнати, що з діагоналями копайлот трохи допоміг, а то я знудився вже міркувати, як ті індекси кляті там крутити. Друга задачка була цікавіша, але легша. Тупо захардкодив у себе індекси знов, хоча варто було б там зробити нормальний пошук 2D-патерна в матриці.
Поки що підсумок такий, що, мабуть, затримаюсь я зі свіфтом ще трохи на погратися. Щось у ньому є все ж.
4272620-3487bc2a
) у мене шансів небагато. Сьогодні ж прокинувся, бо почув, як кіт десь блює Короч попри те, що поспав менше чотирьох годин, не втримався та сів глянути, що за задачки. Писав на Swift
Вибрав її, бо хотів згодом спробувати нею під мікроконтролери щось написати: від сішки ригаю, C++ замахав — хочеться вже щось для душі, тільки б не Python.
Що не сподобалося — повідомлення про помилки. Ну тобто круто, що компілятор багато всього перевіряє, а потім тобі ще й пише, от тільки пише він забагато хуйні. Каже мені, мовляв, не можу викликати функцію з такими параметрами, бо там типи отакі, а воно очікує інші. А потім ще простирадло тексту про те, як він намагався інші варіанти функції підібрати, але нічого не спрацювало — прям як вивід про помилки з шаблонами в C++, їй-богу. Або ще дивіться:
Building for debugging...
error: emit-module command failed with exit code 1 (use -v to see invocation)
~/aoc2024/day4/Sources/day4/main.swift:159:79: error: extra argument 'encoding' in call
if let example = try? String(contentsOfFile: "data/task1.example", encoding: .utf8) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~
~/aoc2024/day4/Sources/day4/main.swift:159:79: error: cannot infer contextual base in reference to member 'utf8'
if let example = try? String(contentsOfFile: "data/task1.example", encoding: .utf8) {
~^~~~
error: fatalError
Кричить на мене прям страшними словами. Виявляється, що не вистачало
import Foundation
на початку, але компілятор мені з цим не підказав.За годину часу, звісно, я повністю мову не відчув. Треба погратися з корутинами хоча б та подивитися, що там ще є. Але задачки для Advent of Code я розвʼязав, а в кінці ще й встиг на стрім пана Соловйова, де він бився з маніпуляціями зі списками в кложі
Поки що підсумок такий, що, мабуть, затримаюсь я зі свіфтом ще трохи на погратися. Щось у ньому є все ж.
Please open Telegram to view this post
VIEW IN TELEGRAM
Cіпласпластик
Останнім часом дуже пізно прокидаюся, тож в AoC-лідербордах (навіть у моєму власному 4272620-3487bc2a) у мене шансів небагато. Сьогодні ж прокинувся, бо почув, як кіт десь блює 😺 🤮 . А потім дивлюся на годинник — майже сьома 🕖 Короч попри те, що поспав менше…
Та-а-а-а-кс… Виявляється, у свіфті є інтероп з C++. Не через С! Можна викликати свіфт-код з плюсів, можна — плюсовий код зі свіфта. Оце вже цікаво!
А ще у мене пет-проєкт під STM, де стоїть вибір між💻 , 💻 та 🕊 . Думаю, спробую тепер якраз останнє там застосувати.
А ще у мене пет-проєкт під STM, де стоїть вибір між
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Alexander Solovyov
на випадок, якщо кложа здається занадто щільною, осьо вам рішення 4.2 (вчорашнього) на uiua:
Xs ← [
"M\WM"_"\WA\W"_"S\WS"
"M\WS"_"\WA\W"_"M\WS"
"S\WM"_"\WA\W"_"S\WM"
"S\WS"_"\WA\W"_"M\WM"
]
/+♭⊞⌕ Xs ⧈∘ 3_3 I
Розповім вам про Red 🔺 . Намагався позавчора розв'язати задачі на Advent of Code за допомогою нього, витратив годину, врешті плюнув та написав найтупіше рішення на Python 💻 за 5 хвилин. Не зайшло якось з редом, бо все забув. А вчора зробив другу спробу.
Про саму мову я дізнався років 7 тому, і одразу закохався. Мою увагу привернув їхній діалект для написання реактивних інтерфейсів — я це обожнюю, як можна було здогадатися з моєї прихильності до QML. Але виявилося, що мова може запропонувати значно більше.
Річ у тім, що там доволі легко створювати власні «діалекти» — фактично eDSL, бо мова уся побудована на блоках, які є і даними, і кодом одночасно, приблизно як у ліспах. Red є розвитком іншої маловідомої мови REBOL. Останній до речі навіть більш популярний за ред в певних ентерпрайз-колах, бо на ньому, як і на реді, можна буквально в декілька рядків наклепати прогу для розв'язання повсякденних задач, та ще й з UI під вінду! Також цікаво, що автори JSON, коли його створювали, надихнулися саме ріболом. З доданих важливих можливостей відносно REBOL в реді є, наприклад, компіляція в машинний код, що робить його значно швидшим. Також є діалект Red/System (а ля💻 ), який дозволяє писати дуже низькорівневий код прямо в реді. Це якраз одна з їхніх selling points: Red здатен задовольнити потреби від низько- до надвисокорівневих.
Тепер про погане. Сайт ледве живий вже бозна-скільки. Роадмап давно пішов нахуй. Коли я починав, актуальна версія була 0.6.4, і вони обіцяли зробити 1.0 протягом року чи півтора. Зараз актуальна версія «0.6.5», бо вони давно забили інкрементувати версії, а просто збирають дейлі-білд та паблішать його. В якийсь момент авторам мови сеча вдарила в голову, і вони вирішили зробити свій блокчейн на реді, а потім навіть, здається, якісь свої токени парили. За декілька років потому написали в блог, мовляв, «все, от тепер точно повертаємося до максимально активної розробки» — і відтоді майже тиша.
Стан мови на сьогодні: асинхронного I/O нема начебто (вони документацію теж хєр забили оновлювати), з мережевою підтримкою щось мутне, підтримки x64 — нема!!! (що одразу ставить хрест на macOS, адже вона більше не вміє запускати x32). Зате є підтримка MS-DOS, наприклад, або Windows XP😂 Також вміє збирати під Raspberry Pi, навіть під Pico, і буцімто під Android.
Колись весь їхній тулчейн був одним бінарем на півтора мегабайта. Саме так, навіть повторю: усе, що вам потрібно було для розробки — це один бінарь! Зараз вони розділили його на три:🦠 Це пиздець, панове.
Короч, обмежився😬
Про саму мову я дізнався років 7 тому, і одразу закохався. Мою увагу привернув їхній діалект для написання реактивних інтерфейсів — я це обожнюю, як можна було здогадатися з моєї прихильності до QML. Але виявилося, що мова може запропонувати значно більше.
Річ у тім, що там доволі легко створювати власні «діалекти» — фактично eDSL, бо мова уся побудована на блоках, які є і даними, і кодом одночасно, приблизно як у ліспах. Red є розвитком іншої маловідомої мови REBOL. Останній до речі навіть більш популярний за ред в певних ентерпрайз-колах, бо на ньому, як і на реді, можна буквально в декілька рядків наклепати прогу для розв'язання повсякденних задач, та ще й з UI під вінду! Також цікаво, що автори JSON, коли його створювали, надихнулися саме ріболом. З доданих важливих можливостей відносно REBOL в реді є, наприклад, компіляція в машинний код, що робить його значно швидшим. Також є діалект Red/System (а ля
Тепер про погане. Сайт ледве живий вже бозна-скільки. Роадмап давно пішов нахуй. Коли я починав, актуальна версія була 0.6.4, і вони обіцяли зробити 1.0 протягом року чи півтора. Зараз актуальна версія «0.6.5», бо вони давно забили інкрементувати версії, а просто збирають дейлі-білд та паблішать його. В якийсь момент авторам мови сеча вдарила в голову, і вони вирішили зробити свій блокчейн на реді, а потім навіть, здається, якісь свої токени парили. За декілька років потому написали в блог, мовляв, «все, от тепер точно повертаємося до максимально активної розробки» — і відтоді майже тиша.
Стан мови на сьогодні: асинхронного I/O нема начебто (вони документацію теж хєр забили оновлювати), з мережевою підтримкою щось мутне, підтримки x64 — нема!!! (що одразу ставить хрест на macOS, адже вона більше не вміє запускати x32). Зате є підтримка MS-DOS, наприклад, або Windows XP
Колись весь їхній тулчейн був одним бінарем на півтора мегабайта. Саме так, навіть повторю: усе, що вам потрібно було для розробки — це один бінарь! Зараз вони розділили його на три:
red-toolchain
для компіляції, red-cli
для термінального REPL та red-view
— для гуїшного. Я хотів собі стягнути red-cli
, щоб легше було використовувати у VS Code, але вінда не дала це зробити, бо задетектила там троян! Короч, обмежився
red-view
та red-toolchain
, бо на них принаймні не скаржиться, але досі сумніваюся, чи не майнитиме мій комп віднині їхні всраті токени Please open Telegram to view this post
VIEW IN TELEGRAM
Cіпласпластик
Розповім вам про Red 🔺 . Намагався позавчора розв'язати задачі на Advent of Code за допомогою нього, витратив годину, врешті плюнув та написав найтупіше рішення на Python 💻 за 5 хвилин. Не зайшло якось з редом, бо все забув. А вчора зробив другу спробу. Про…
Тепер власне щодо використання та задач.
Весь синтаксис мови — це купка символів на кшталт дужок і ще декількох типу двокрапки та апострофа. Решта (всі оператори, конструкції для control flow, функції стандартної ліби тощо) гіпотетично могли б бути описані чисто за допомогою них. На практиці ж заради швидкодії деякі функції мають нативну імплементацію, деякі описані в Red/System, а деякі вже на чистому високорівневому Red. Я писав тільки на останньому. Зате згадав і використав їхній хвалений parse-діалект, який дозволяє описувати граматику (PEG). Не те щоб у задачах це сильно треба було, але мені кортіло.
Писати цією мовою я настільки задовбався (не через синтаксис — про це далі), що врешті накодив максимально тупий брутфорс для пошуку рішень. Перша прораховується миттєво, а от для другої моя реалізація вже не дуже канає. Коли я запустив рішення другої на інтерпретацію, воно виконувалося 25 хвилин🤣 Я там забув дебажний рядок прибрати, який на кожну ітерацію відкривав файл та писав у нього, і було вже якось впадлу перезапускати. Втім не думаю, що це щось значно змінило б.
Доки воно проганяло це рішення, я спробував скомпілювати прогу в нативний бінарь. Довелося виправити пару помилок, які цьому заважали, та я встиг і пофіксити, і скомпілювати, і запустити, і отримати результат — все доки інтерпретація тривала ) Вислідний бінарь під вінду займає 904 КБ і не має жодних залежностей. Як на мене, вельми непогано!
Чи можна зробити рішення швидшим? Я певен, що так. Бачу низку можливостей. Але… Найбільше сповільнює відсутність досвіду. Вкрай боляче писати мовою, коли доводиться буквально шукати, як проітерувати послідовність або свопнути дві змінні😵💫 Однак проміж роботою та побутовими справами я за вечір та пів дня прочитав 90% книжки про Red, що трохи освіжило деякі знання.
У підсумку: ця мова мені досі дуже подобається! Дати б собі трохи більше часу з нею — і це пісня. Дуже прикро та болісно, що її розвитком не займаються й уже скоріш за все не почнуть. Надії вже не маю.
Весь синтаксис мови — це купка символів на кшталт дужок і ще декількох типу двокрапки та апострофа. Решта (всі оператори, конструкції для control flow, функції стандартної ліби тощо) гіпотетично могли б бути описані чисто за допомогою них. На практиці ж заради швидкодії деякі функції мають нативну імплементацію, деякі описані в Red/System, а деякі вже на чистому високорівневому Red. Я писав тільки на останньому. Зате згадав і використав їхній хвалений parse-діалект, який дозволяє описувати граматику (PEG). Не те щоб у задачах це сильно треба було, але мені кортіло.
Писати цією мовою я настільки задовбався (не через синтаксис — про це далі), що врешті накодив максимально тупий брутфорс для пошуку рішень. Перша прораховується миттєво, а от для другої моя реалізація вже не дуже канає. Коли я запустив рішення другої на інтерпретацію, воно виконувалося 25 хвилин
Доки воно проганяло це рішення, я спробував скомпілювати прогу в нативний бінарь. Довелося виправити пару помилок, які цьому заважали, та я встиг і пофіксити, і скомпілювати, і запустити, і отримати результат — все доки інтерпретація тривала ) Вислідний бінарь під вінду займає 904 КБ і не має жодних залежностей. Як на мене, вельми непогано!
Чи можна зробити рішення швидшим? Я певен, що так. Бачу низку можливостей. Але… Найбільше сповільнює відсутність досвіду. Вкрай боляче писати мовою, коли доводиться буквально шукати, як проітерувати послідовність або свопнути дві змінні
У підсумку: ця мова мені досі дуже подобається! Дати б собі трохи більше часу з нею — і це пісня. Дуже прикро та болісно, що її розвитком не займаються й уже скоріш за все не почнуть. Надії вже не маю.
Please open Telegram to view this post
VIEW IN TELEGRAM
Трохи забарився я з вирішенням задач на Advent of Code через відрядження — зараз наздоганяю. Але розвʼязав того тижня задачі восьмого дня на Nim 👑 , про який скажу пару слів.
Вже не згадаю, як саме я на цю мову вийшов колись, але зацікавився одразу. Аж настільки, що купив паперову книгу. Прочитав її, покодив трохи й закинув. Проблема була не стільки у мові, скільки у її розвитку. Якось не відчувалося зрілості в головних мейнтейнерах, версії 1.0 знов-таки не було досі (а мене прям так і тягне на сире лайно: Rust у свій час, потім Nim, Red тощо), ну й купа інших сумнівів.
Але! Версію 1.0 вони все-таки зробили. А потім і 2.0. У мене ось зараз 2.2 стоїть вже. І знаєте шо? А норм!
Синтаксис — така собі суміш Pascal та Python. Є дженеріки, в яких доволі легко обмежити тип якоюсь підмножиною. Я для розвʼязання задачі оперував 2D-координатами: варто було б зробити окремий тип на це, та я обмежився кортежами. Ось, наприклад, перевантажений під мою задачу оператор
Дуже лаконічно, але при цьому легко читається, як на мене.
Інша прикольна фіча — Uniform Function Call Syntax. Зазвичай в ООП-мовах, якщо хочете додати метод то якогось типу, ви мусите його описати в самому класі. Деякі мови пропонують різні рішення, щоб це покращити: partial classes, mixins, traits тощо. В Nim ви просто пишете звичайну функцію на кшталт
Окрім цього я вже колись згадував (і в статті) про
Ну й наостанок скажу, що в Nim прям дуже легко писати код, який виконуватиметься під час компіляції. Фактично майже нічого й робити не треба: інколи хіба що прагму до функції додати, інколи кудись😆
Короч, приємна доволі мова. Навіть хз, чому я не використовую.
Вже не згадаю, як саме я на цю мову вийшов колись, але зацікавився одразу. Аж настільки, що купив паперову книгу. Прочитав її, покодив трохи й закинув. Проблема була не стільки у мові, скільки у її розвитку. Якось не відчувалося зрілості в головних мейнтейнерах, версії 1.0 знов-таки не було досі (а мене прям так і тягне на сире лайно: Rust у свій час, потім Nim, Red тощо), ну й купа інших сумнівів.
Але! Версію 1.0 вони все-таки зробили. А потім і 2.0. У мене ось зараз 2.2 стоїть вже. І знаєте шо? А норм!
Синтаксис — така собі суміш Pascal та Python. Є дженеріки, в яких доволі легко обмежити тип якоюсь підмножиною. Я для розвʼязання задачі оперував 2D-координатами: варто було б зробити окремий тип на це, та я обмежився кортежами. Ось, наприклад, перевантажений під мою задачу оператор
-
(мінус):proc `-`[T: int32 | int64](a, b: (T, T)): (T, T) =
((a[0] - b[0]), (a[1] - b[1]))
Дуже лаконічно, але при цьому легко читається, як на мене.
Інша прикольна фіча — Uniform Function Call Syntax. Зазвичай в ООП-мовах, якщо хочете додати метод то якогось типу, ви мусите його описати в самому класі. Деякі мови пропонують різні рішення, щоб це покращити: partial classes, mixins, traits тощо. В Nim ви просто пишете звичайну функцію на кшталт
add
, а потім можете її визивати хоч як add(a, b)
, хоч як a.add(b)
— ці записи тотожні. Для C++ до речі теж є аналогічна пропозиція, але так досі нічого й не впровадили.Окрім цього я вже колись згадував (і в статті) про
discard
. В тому ж C++ можна позначити функцію, яка повертає значення, як [[nodiscard]]
, і тоді її значення не можна проігнорувати без ворнінга. А в Nim навпаки! Якщо функція щось повертає, а ви це значення не використали, то компілятор одразу помилку пише, якщо не проставити явно discard
.Ну й наостанок скажу, що в Nim прям дуже легко писати код, який виконуватиметься під час компіляції. Фактично майже нічого й робити не треба: інколи хіба що прагму до функції додати, інколи кудись
const
поставити. Я у своїй програмі трохи читернув заради швидкодії: я там читаю файл з даними та будую з нього матрицю — все на етапі компіляції. Якщо трохи заморочитися, то можна було б взагалі все рішення обрахувати в компайл-таймі, а прога при запуску його просто в консоль виводитиме Короч, приємна доволі мова. Навіть хз, чому я не використовую.
Please open Telegram to view this post
VIEW IN TELEGRAM
Девʼятий день не було часу вже розвʼязувати того тижня, тому вирішив написати знов на Haskell. І написав… першу задачу. А на другій нормально так похавав лайна — плюнув та наразі скіпнув. Колись повернуся та перепишу на щось інше. А сьогодні пару слів про Go 💻 , який я використав для десятого дня. (Зараз насправді вже 17-й, а я на 15-му — трохи відстаю досі).
Я колись вже коригував чийсь нескладний код на Go, але свого з нуля не писав. Зараз спробував — думку не змінив: проста мова, доволі легко читається, легко пишеться, на виході сінгл бінарь без залежностей. Клас.
Але-е-е-е. Мова також максимально нудна! Навіть в Python можна хоча б декораторами обмазатися, щоб зробити упорото, зате красиво. А тут тупо ніфіга нема.
Треба було множину точок зробити (set) — в стандартній лібі немає. Довелося емулювати через🤮 (це типу мапінг з точок в порожні структури).
Захотілося фільтранути список по предикату — фільтра немає😵 Ок-ок, раніше не було дженеріків. До речі не уявляю, як люди жили в статично-типізованій мові без дженериків — жах. Наче Java 2. Та зараз-то вони є! Очікуєш, що ось нині-то подобавляють нарешті зручностей! А ніфіга. Накостилив свій:
Таке короч. Жити можна, але хз. Наче жуйка, що втратила смак пів години тому.
Я колись вже коригував чийсь нескладний код на Go, але свого з нуля не писав. Зараз спробував — думку не змінив: проста мова, доволі легко читається, легко пишеться, на виході сінгл бінарь без залежностей. Клас.
Але-е-е-е. Мова також максимально нудна! Навіть в Python можна хоча б декораторами обмазатися, щоб зробити упорото, зате красиво. А тут тупо ніфіга нема.
Треба було множину точок зробити (set) — в стандартній лібі немає. Довелося емулювати через
map[point]struct{}
Захотілося фільтранути список по предикату — фільтра немає
func filter[T any](input []T, predicate func(T) bool) []T {
result := []T{}
for _, item := range input {
if predicate(item) {
result = append(result, item)
}
}
return result
}
Таке короч. Жити можна, але хз. Наче жуйка, що втратила смак пів години тому.
Please open Telegram to view this post
VIEW IN TELEGRAM
Робив задачі одинадцятого дня на Elixir 💻 . Хотів колись опанувати цю мову, щоб писати ботів для телеграму не на Python, тож пару років тому сів за Erlang 💻 (не шукаю простих шляхів — вирішив йти від «початку»). Але врешті закинув.
Зараз же відкинув принциповість, просто сів та почав писати. І мені дуже сподобалося. Це буквально Erlang з нормальним синтаксисом. Дуже подобається пайп
Не обійшлося й без WTF-моментів. Наприклад, функції не можуть бути використані в гардах інших функцій чи в
Звісно, у моєму випадку писати на Elixir сенсу було не надто багато, бо він не в таких задачах себе найкраще проявляє. Я там намагався трохи погратися з багатьма процесами, але врешті дропнув цей шматок коду. Втім чисто відчути мову мені вистачило. Я подумки це застосував до того, що вже знаю про Erlang, і загальна картина мені подобається — точно не останній мій раз використання Elixir.
Зараз же відкинув принциповість, просто сів та почав писати. І мені дуже сподобалося. Це буквально Erlang з нормальним синтаксисом. Дуже подобається пайп
|>
(хоча звичайна крапка все одно була б краще, бо одне натискання на клавіатурі замість трьох), подобається зіставлення з шаблоном тощо. Гляньте ось:def solve_like_noob(iterations) do
data = read_data(@file_path)
1..iterations
|> Enum.reduce(data, fn _, acc ->
acc |> Enum.flat_map(&replace_naïve/1)
end)
|> length()
end
Не обійшлося й без WTF-моментів. Наприклад, функції не можуть бути використані в гардах інших функцій чи в
case
— можна тільки макроси. Мабуть, якась внутрішня специфіка. Але не сильно заважало.Звісно, у моєму випадку писати на Elixir сенсу було не надто багато, бо він не в таких задачах себе найкраще проявляє. Я там намагався трохи погратися з багатьма процесами, але врешті дропнув цей шматок коду. Втім чисто відчути мову мені вистачило. Я подумки це застосував до того, що вже знаю про Erlang, і загальна картина мені подобається — точно не останній мій раз використання Elixir.
Please open Telegram to view this post
VIEW IN TELEGRAM
Ну шо сказати. Втомився я трохи. Мало того, що відстав майже на тиждень (зараз лише 2,5 дні наздогнати) і що пишу щоразу новими для себе мовами, так і дописи, як оцей, займають багацько часу. Але хто, якщо не я?! )
Отже 12-й день розвʼязував на Dart💻 . Хотів глянути на мову, бо існує Flutter 💻 : як би я не любив QML, є ненульовий шанс, що рано чи пізно він накриється, тож треба дивитися альтернативи.
Підсумок такий: Dart — це JavaScript, яким він мав бути. І ні, не тому, що JS поганий, а цей класний, а тому, що тоді б принаймні назва була виправдана. Бо від Dart дуже стійке відчуття, наче пишеш на Java з дрібними домішками інших мов того ж типу.
І це якось мене бентежить. Типу, сідаєш такий пописати на релаксі, а воно тобі хуяк по обличчю оцим своїм синтаксисом. Ну, типу, якби я хотів постраждати, то я б, мабуть, одразу взяв нормальну мову, яка компілюється не в JS, м? Хз, короч.
Ну, принаймні дженеріки є та навіть Set у стандартній лібі, а не як в Go — гугл перевершив себе цього разу. Ось рандомні шматки коду, щоб ви трохи відчули синтаксис:
Ну ви зрозуміли.
З приємного: доволі якісний тулінг, зокрема плагін для vscode. Працює швидко й ніби безпроблемно.
Сама по собі мова не складна й доволі непогано структурована. Як нині модно, є константи компіляції (
З цікавого: підтримка hot reload, утім хз, чи це фіча мови, чи чисто Flutter. Так чи інакше таке нескладно зробити, коли транслюєшся в JavaScript.
Та попри все це мова якась неприємна мені. Якась суха й без життя. В ентерпрайзах на такій писати якраз норм, мабуть. Як-он Toyota з їхньою infotaintment-системою. Але я за можливості уникатиму.
Чи то мене просто гугл аж настільки дратує?
Отже 12-й день розвʼязував на Dart
Підсумок такий: Dart — це JavaScript, яким він мав бути. І ні, не тому, що JS поганий, а цей класний, а тому, що тоді б принаймні назва була виправдана. Бо від Dart дуже стійке відчуття, наче пишеш на Java з дрібними домішками інших мов того ж типу.
І це якось мене бентежить. Типу, сідаєш такий пописати на релаксі, а воно тобі хуяк по обличчю оцим своїм синтаксисом. Ну, типу, якби я хотів постраждати, то я б, мабуть, одразу взяв нормальну мову, яка компілюється не в JS, м? Хз, короч.
Ну, принаймні дженеріки є та навіть Set у стандартній лібі, а не як в Go — гугл перевершив себе цього разу. Ось рандомні шматки коду, щоб ви трохи відчули синтаксис:
int discount_cost() {
return area * edges().length;
}
Set<Edge> edges() {
Edge? findEdge(Point point, Side side) {
const check = {
Side.Above: Point(0, -1),
Side.Below: Point(0, 1),
Side.Left: Point(-1, 0),
Side.Right: Point(1, 0),
};
// ...
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is! Point) return false;
return row == other.row && col == other.col;
}
@override
String toString() => '$start -> $end ($direction)';
Matrix<bool> visited =
Matrix(List.generate(rows, (_) => List.generate(cols, (_) => false)));
Ну ви зрозуміли.
З приємного: доволі якісний тулінг, зокрема плагін для vscode. Працює швидко й ніби безпроблемно.
Сама по собі мова не складна й доволі непогано структурована. Як нині модно, є константи компіляції (
const
), рантайм-константи (final
) та змінні (var
). Є інтерполяція рядків, nullable-типи, скорочений синтаксис для методів (через =>
), ну й усяке таке. Є якийсь свій прикол для тредів, який вони назвали ізолятами (isolates), але мені не довелося використати.З цікавого: підтримка hot reload, утім хз, чи це фіча мови, чи чисто Flutter. Так чи інакше таке нескладно зробити, коли транслюєшся в JavaScript.
Та попри все це мова якась неприємна мені. Якась суха й без життя. В ентерпрайзах на такій писати якраз норм, мабуть. Як-он Toyota з їхньою infotaintment-системою. Але я за можливості уникатиму.
Please open Telegram to view this post
VIEW IN TELEGRAM
Тринадцятого дня була доволі проста, але цікава задача. Треба керувати роборукою в ігровому автоматі, з яких іграшки дістають, знаєте такі? Claw machine англійською. Вам дають отакий інпут (їх у файлі багато):
Натискання на A коштує 3 токени, а на B — 1 токен. Треба витягнути приз зі вказаних координат так, щоб було найдешевше. Я глянув на це, і щось стало та-а-а-ак впадлу казати комʼютеру крок за кроком, як розвʼязати цю систему рівнять, що я вирішив натомість просто пояснити йому, що треба, а він нехай сам дуплиться. І ні, я не про LLM.
Я вирішив писати на Prolog🦉 ! Зараз і вас навчу.
Раніше я вже робив три чи чотири підходи до цієї мови, і щоразу невдало. Прям відчувався брак теоретичної бази. В універі нас цьому не вчили на жаль — там з цікавого був тільки Haskell (десь між лабами по VHDL та створенням формочок в MFC).
Всі мануали — це також якесь дно. Задовбало читати типові приклади про те, хто там кому батько та чий собака — друг Сократа. Бо підсвідомо розумієш, що між цими прикладами та практичним застосуванням ціла прірва невідомого. Треба було щось міняти.
Тож для початку я все ж звернувся до ШІ-шки та попросив написати, як виглядатиме читання з файлу. І раптом мене осяяло, як це працює!
Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Натискання на A коштує 3 токени, а на B — 1 токен. Треба витягнути приз зі вказаних координат так, щоб було найдешевше. Я глянув на це, і щось стало та-а-а-ак впадлу казати комʼютеру крок за кроком, як розвʼязати цю систему рівнять, що я вирішив натомість просто пояснити йому, що треба, а він нехай сам дуплиться. І ні, я не про LLM.
Я вирішив писати на Prolog
Раніше я вже робив три чи чотири підходи до цієї мови, і щоразу невдало. Прям відчувався брак теоретичної бази. В універі нас цьому не вчили на жаль — там з цікавого був тільки Haskell (десь між лабами по VHDL та створенням формочок в MFC).
Всі мануали — це також якесь дно. Задовбало читати типові приклади про те, хто там кому батько та чий собака — друг Сократа. Бо підсвідомо розумієш, що між цими прикладами та практичним застосуванням ціла прірва невідомого. Треба було щось міняти.
Тож для початку я все ж звернувся до ШІ-шки та попросив написати, як виглядатиме читання з файлу. І раптом мене осяяло, як це працює!
Please open Telegram to view this post
VIEW IN TELEGRAM
У Prolog 🦉 фішка, що все описується предикатами. А ми з вами звикли, що предикат — це функція, яка повертає
Є, наприклад, предикат
Можна перевіряти, чи число співпадає зі своїм представленням у вигляді рядка. Зручно, але шо далі? А ось що:
Тобто ви просто ставите замість одного з параметрів змінну (з великої літери), а воно, якщо може, матчить його зі значенням, з яким предикат видаватиме
Ось парсинг першого рядка вхідних даних, що я давав на початку попереднього допису:
У
Тут три параметри — назва координати (
Ну короч ось так я запарсив увесь файл, а потім читернув та використав солвер лінійних рівнянь ))
Він «отримує» перші шість параметрів у вигляді чисел і підбирає й «виводить» такі A й B, щоб ціна була найменша. Вуаля! )
Звісно, Prolog цим не обмежується. Теоретична база все одно потрібна, бо він і так доволі повільний (пів секунди на дві задачі), а на додачу дуже легко запитати щось, відповідь на що він шукатиме мільярд років. Попри це я зацінив!
І навіть при розвʼязанні задач на 17-й день іншою мовою, я у момент відчаю швиденько написав на Prolog робочу віртуальну машину на 8 команд з трьома регістрами памʼяті, щоб звірити результати.
Дуже раджу теж спробувати.
bool
. І я все ніяк не міг зрозуміти, а як же звідти щось інше діставати. Зрозуміло, що там зіставлення з шаблоном, як в якомусь Haskell чи Erlang, але все одно. Навіть дивився приклади — та не вкладалося в голові. Розповідаю, отже.Є, наприклад, предикат
number_string
. Працює отак:?- number_string(42, "42").
true.
Можна перевіряти, чи число співпадає зі своїм представленням у вигляді рядка. Зручно, але шо далі? А ось що:
?- number_string(42, Str).
Str = "42".
?- number_string(Num, "42").
Num = 42.
Тобто ви просто ставите замість одного з параметрів змінну (з великої літери), а воно, якщо може, матчить його зі значенням, з яким предикат видаватиме
true
. Причому це можна робити в обидва боки! Іншими словами, у вас параметри предиката — це і «вхідні», і «вихідні» разом. How cool is that?!Ось парсинг першого рядка вхідних даних, що я давав на початку попереднього допису:
parse_button_a(Line, X, Y) :-
split_string(Line, ",:", " ", ["Button A", Xs, Ys]),
parse_num('X', Xs, X),
parse_num('Y', Ys, Y).
У
parse_button_a
я за задумом першим параметром даю текстовий рядок й очікую, що X та Y воно виведе само. Для цього мають виконуватися правила під ним. Перше правило: якщо я цей рядок сплітаю по комі та двокрапці (і прибираю зайві пробіли), то очікую, що останній параметр заматчиться зі списком з трьох штук. Перша — це сталий рядок "Button A"
, а другий та третій — це рядки зі значеннями для координат (наприклад, "X+94"
та "Y+34"
). Два наступних правила вже працюють якраз з ними й фактично дістають з рядків числа. Глянемо всередину.parse_num(Var, String, Number) :-
atom_chars(String, [Var, Sign|Rest]),
atom_chars(NumStr, [Sign|Rest]),
atom_number(NumStr, Number).
Тут три параметри — назва координати (
Var
), рядок з попереднього правила та відповідне числове значення. atom_chars
матчить рядок зліва зі списком атомів (а ля констант) справа. Тож перший з них бере рядок і деконструює його на Var
('X'
), Sign
('+'
) і решту (['9', '4']
). Друге правило фактично робить це у зворотній бік: справа ми даємо ['+', '9', '4']
, а зліва отримуємо '+94'
. Цими двома правилами ми, по-перше, переконалися, що це справді X
, який ми очікуємо, а по-друге, ми його відрізали з рядка, щоб не заважав. А третій предикат матчить атом (вважайте, що рядок) зліва ('+94'
) з числом справа (там буде 94
). І потім, якщо все це заматчилося, це число йде в параметри самого parse_num
. Ну короч ось так я запарсив увесь файл, а потім читернув та використав солвер лінійних рівнянь ))
solve_machine(Ax, Ay, Bx, By, Px, Py, A, B) :-
A in 0..100,
B in 0..100,
A * Ax + B * Bx #= Px,
A * Ay + B * By #= Py,
Tokens #= A * 3 + B,
labeling([min(Tokens)], [A,B]).
Він «отримує» перші шість параметрів у вигляді чисел і підбирає й «виводить» такі A й B, щоб ціна була найменша. Вуаля! )
Звісно, Prolog цим не обмежується. Теоретична база все одно потрібна, бо він і так доволі повільний (пів секунди на дві задачі), а на додачу дуже легко запитати щось, відповідь на що він шукатиме мільярд років. Попри це я зацінив!
І навіть при розвʼязанні задач на 17-й день іншою мовою, я у момент відчаю швиденько написав на Prolog робочу віртуальну машину на 8 команд з трьома регістрами памʼяті, щоб звірити результати.
Дуже раджу теж спробувати.
Please open Telegram to view this post
VIEW IN TELEGRAM
Про мову Ada я чув ду-у-у-уже давно і весь цей час мав враження, що вона давно померла й забута. Аж доки кілька років тому мій друг @repa4ok не розповів мені протилежне (дякую!). Мова розвивається (останній стандарт — 2022 року), спільнота розвивається, навіть менеджер пакетів є.
Мову колись створили для військових і назвали на честь першої програмістки у світі (так, першою була жінка, якщо хто не знав). Вирішив нарешті дати цій мові шанс і спробувати її для розвʼязання задач 14-го дня.
Синтаксис дуже схожий на Pascal (принаймні той, який памʼятаю я). Писати нею доволі важко, коли бачиш її вперше, бо є велике різноманіття конструкцій, а ментальної моделі для них в голові бракує. Попри це читається такий код вкрай легко — тут вам не C++ і, боже збав, не Rust.
Компілятор скаржиться на неправильне форматування, тож краще одразу налаштувати собі автоформат на збереження файлу. З цікавого: за стайл-гайдом відступи — 3 (три!) пробіли🙃 Ще компілятор доволі легко знаходить похибки в тексті й каже щось адекватніше за «unexpected token»:
Помилка:
Але найголовніше, що мене до душі — це опис власних типів. Наприклад, в Ada не прийнято всюди використовувати «голий»
За умовами задачі треба було змоделювати переміщення роботів. Одна з цікавинок саме задач цього дня: коли робот доходить то стіни, то автоматично виходить з іншого боку, як часто роблять в іграх. І так зійшлося, що в Ada це можна описати!
Ось це буквально значить, що координата X змінюється від 0 до 100 (
Ще в мові є дженерики (трохи використав), є ООП (не бачив), є якісь засоби для формальної верифікації програм тощо. Дуже багато всього, але мені на жаль поки нема чого ще додати, бо я пів дня з нею всього посидів.
Точно повернуся до💻 , тим паче що, як виявилося, існує тулчейн під Xtensa-проци, які стоять в ESP32, а в мене якраз пет-проєкти на них.
Мову колись створили для військових і назвали на честь першої програмістки у світі (так, першою була жінка, якщо хто не знав). Вирішив нарешті дати цій мові шанс і спробувати її для розвʼязання задач 14-го дня.
Синтаксис дуже схожий на Pascal (принаймні той, який памʼятаю я). Писати нею доволі важко, коли бачиш її вперше, бо є велике різноманіття конструкцій, а ментальної моделі для них в голові бракує. Попри це читається такий код вкрай легко — тут вам не C++ і, боже збав, не Rust.
Компілятор скаржиться на неправильне форматування, тож краще одразу налаштувати собі автоформат на збереження файлу. З цікавого: за стайл-гайдом відступи — 3 (три!) пробіли
procedure Solve_First is
package IO renames Ada.Text_IO;
begin
delclare
Counter : Integer := 0;
-- and so on...
Помилка:
Compile
[Ada] day_14.adb
day_14.adb:8:07: error: incorrect spelling of keyword "declare"
Але найголовніше, що мене до душі — це опис власних типів. Наприклад, в Ada не прийнято всюди використовувати «голий»
Integer
, і це правильно, бо він може значити що завгодно. Натомість ви «моделюєте» домен вашої задачі у вигляді типів, а компілятор вам допомагає їх дотримуватися (і лупить по руках, коли ви цього не робите). Ось шматочок мого коду:Width : constant := 101;
Height : constant := 103;
type X_Coordinate is mod Width;
type Y_Coordinate is mod Height;
type Matrix is array (X_Coordinate, Y_Coordinate) of Integer
with Default_Component_Value => 0;
type X_Movement is range -Width .. Width;
type Y_Movement is range -Height .. Height;
type Point is record
X : X_Coordinate;
Y : Y_Coordinate;
end record;
type Velocity is record
DX : X_Movement;
DY : Y_Movement;
end record;
type Robot is record
Pos : Point;
Vel : Velocity;
end record with Put_Image => Robot_Put_Image;
За умовами задачі треба було змоделювати переміщення роботів. Одна з цікавинок саме задач цього дня: коли робот доходить то стіни, то автоматично виходить з іншого боку, як часто роблять в іграх. І так зійшлося, що в Ada це можна описати!
type X_Coordinate is mod Width;
Ось це буквально значить, що координата X змінюється від 0 до 100 (
Width - 1
), а якщо раптом стає більше або менше, то врапається на нове коло. З іншого боку жодних автоматичних приведень типів немає, як і в Haskell, тож всі конвертації тільки ручками. До речі зараз вже бачу, що деякі типи можна було описати краще.Ще в мові є дженерики (трохи використав), є ООП (не бачив), є якісь засоби для формальної верифікації програм тощо. Дуже багато всього, але мені на жаль поки нема чого ще додати, бо я пів дня з нею всього посидів.
Точно повернуся до
Please open Telegram to view this post
VIEW IN TELEGRAM
Сьогодні не про Advent of Code, хоча в мене ще лишилася пара повʼязаних тем на обговорення (днями напишу).
За порадою пана @dmytrish вирішив покодити задачі 17-го дня на Crystal. Статично типізована мова з синтаксисом Ruby — звучить чудово.
Перша задача полягала в написанні власної віртуальної машини з трьома регістрами, про що я вже згадував у кінці допису про Prolog. Це було легко й відносно приємно. Хоча вже тут стало ясно, що синтаксис настільки ж лаконічним зробити не вийде. Наприклад, там є 8 команд, що виконують певні операції над регістрами. При цьому сама вхідна «програма» записана тупо числами, тому природне бажання — зробити мапку з чисел на відповідні функції. Очікуєш щось отаке:
Насправді ж найкоротше, що я зміг написати, щоб це запрацювало:
Довелося додати зовсім трошки візуального сміття, щоб компілятор збагнув, що я від нього хочу. Мабуть, звичайний switch-case (який тут називається case-when) був би навіть краще.
Різноманіття числових типів — це окрема розмова. Є від
Компілятор бачить, що функція приймає
Ідемо далі. Рівночасність підтримується, але реалізується через fibers (а ля корутини), які спілкуються через channels. Наче норм, але тредів немає — все працює в одному (і ще GC у власному), тож розпаралелити якісь обчислення таким чином не вийде, як я зрозумів.
Ну а в решті моментів Crystal — це Ruby з типами. Я сам на Ruby останній раз писав, коли була ще версія 1.9, і, якщо чесно, я б зараз був переконаний, що мова давно вмерла, якби нею не писав автор каналу Стендап Сьогодні пан Шевцов та зрідка не згадував у себе в каналі пан Рожков.
У Ruby мені завжди подобалася легкість створення власних DSL. І наскільки я можу судити, Crystal це також вміє. Наприклад, тут вже є так званий Spec, який дозволяє писати BDD-тести. Мені дуже стало в пригоді. Пишеш щось на кшталт:
Запускаєш
До речі щодо створення. Вельми непоганий тулінг у Crystal! Взагалі не було з цим проблем.
Короч, підсумок: мова наче й непогана, компілюється, перевіряє типи, працює явно швидше за Ruby, проте, з іншого боку не зовсім ясно на кого вона орієнтована. Хто і для чого її взагалі використовує? Є тут хтось, хто пише нею?
P.S. Усіх з Різдвом, до речі🎄
Перша задача полягала в написанні власної віртуальної машини з трьома регістрами, про що я вже згадував у кінці допису про Prolog. Це було легко й відносно приємно. Хоча вже тут стало ясно, що синтаксис настільки ж лаконічним зробити не вийде. Наприклад, там є 8 команд, що виконують певні операції над регістрами. При цьому сама вхідна «програма» записана тупо числами, тому природне бажання — зробити мапку з чисел на відповідні функції. Очікуєш щось отаке:
@@ops = { 0 => adv!, 1 => bxl!, 2 => bst!, 3 => jnz!, 4 => bxc!, 5 => out!, 6 => bdv!, 7 => cdv! }
Насправді ж найкоротше, що я зміг написати, щоб це запрацювало:
@@ops : Hash(Int8, Proc(Day17Solver, Int64, Int16)) = {
0_i8 => ->(s : Day17Solver, v : Int64) { s.adv!(v) },
1_i8 => ->(s : Day17Solver, v : Int64) { s.bxl!(v) },
2_i8 => ->(s : Day17Solver, v : Int64) { s.bst!(v) },
3_i8 => ->(s : Day17Solver, v : Int64) { s.jnz!(v) },
4_i8 => ->(s : Day17Solver, v : Int64) { s.bxc!(v) },
5_i8 => ->(s : Day17Solver, v : Int64) { s.out!(v) },
6_i8 => ->(s : Day17Solver, v : Int64) { s.bdv!(v) },
7_i8 => ->(s : Day17Solver, v : Int64) { s.cdv!(v) },
}
Довелося додати зовсім трошки візуального сміття, щоб компілятор збагнув, що я від нього хочу. Мабуть, звичайний switch-case (який тут називається case-when) був би навіть краще.
Різноманіття числових типів — це окрема розмова. Є від
Int8
до Int128
і від UInt8
до UInt128
, клас! Але коли ти пишеш звичайний числовий літерал на кшталт 42
, то це завжди Int32
. Тобто хочу я в тесті передати у свій розвʼязувач задачі «програму» як список чисел, але я не можу написати це як:[0,3,5,4,3,0]
Компілятор бачить, що функція приймає
Array(Int8)
, і він, мабуть, бачить, що всі ці числа легко влізають в Int8
, але ні: «Не можу, — каже, — конвертнути Int32
в Int8
». Доводиться писати отак:[0_i8,3_i8,5_i8,4_i8,3_i8,0_i8]
Ідемо далі. Рівночасність підтримується, але реалізується через fibers (а ля корутини), які спілкуються через channels. Наче норм, але тредів немає — все працює в одному (і ще GC у власному), тож розпаралелити якісь обчислення таким чином не вийде, як я зрозумів.
Ну а в решті моментів Crystal — це Ruby з типами. Я сам на Ruby останній раз писав, коли була ще версія 1.9, і, якщо чесно, я б зараз був переконаний, що мова давно вмерла, якби нею не писав автор каналу Стендап Сьогодні пан Шевцов та зрідка не згадував у себе в каналі пан Рожков.
У Ruby мені завжди подобалася легкість створення власних DSL. І наскільки я можу судити, Crystal це також вміє. Наприклад, тут вже є так званий Spec, який дозволяє писати BDD-тести. Мені дуже стало в пригоді. Пишеш щось на кшталт:
describe "#parse_registers" do
it "returns a hash of registers" do
registers = [
"Register A: 729",
"Register B: 2",
"Register C: 3",
]
Day17Solver.new.parse_registers(registers).should eq({a: 729, b: 2, c: 3}.to_h)
end
end
describe "runs" do
it "the first example and outputs 4,6,3,5,6,3,5,2,1,0" do
solver = Day17Solver.new "data/task.example"
solver.run
solver.@output.should eq([4, 6, 3, 5, 6, 3, 5, 2, 1, 0])
end
end
Запускаєш
crystal spec
, і воно все перевіряє й видає результат. Дуже зручно, бо не треба якісь окремі ліби підʼєднувати чи щось налаштовувати — воно одразу є при створенні проєкту.До речі щодо створення. Вельми непоганий тулінг у Crystal! Взагалі не було з цим проблем.
Короч, підсумок: мова наче й непогана, компілюється, перевіряє типи, працює явно швидше за Ruby, проте, з іншого боку не зовсім ясно на кого вона орієнтована. Хто і для чого її взагалі використовує? Є тут хтось, хто пише нею?
P.S. Усіх з Різдвом, до речі
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Щоразу, як дивлюся або читаю пана Соловйова, хочеться спробувати Clojure, особливо на тлі незрозумілостей з Red.
Ліспи з їхньою купою варіацій і REBOL з наразі єдиним нащадком Red — це D&D🎮 світу програмування. Є десяток базових правил, а далі все обмежується лише вашою фантазією (і спроможностями заліза гг). Обидва мають суперпростий синтаксис — можна навіть сказати, що ніякого синтаксису немає: ви просто пишете слова, а потім наділяєте їх власними значеннями. Звучить як казка.
Я вже намагався зробити підхід до Clojure десять років тому, але вочевидь був неготовий ментально. Зараз же зробив другу спробу і… не зміг себе пересилити: дивлюся в цей короткий лаконічний код, а подумки бачу, як JVM нестямно жере памʼять мого компа, і уявляю, як мені доведеться йти крізь ієрархію тек, щоб нарешті знайти там свій єдиний файл з кодом.
На щастя з одного зі стрімів пана Соловйова я дізнався про існування мови Janet👩🦰 — дуже схожого на кложу діалекту ліспа, який написаний на сішці, важить майже нічого, легко інтегрується з сішним кодом в обидва боки тощо. І спробував її для задач на пʼятнадцятий день!
На початку було дуже складно, зокрема через відсутність навичок роботи з реплом та редагування лісп-коду. «Кляті дужки!»😂 Без paredit погано, з paredit погано — про це окремо ще розповім.
Одним із сюрпризів став той факт, що на ліспі взагалі-то можна (і дуже легко) писати імперативно. Чогось був переконаний, що це чисто ФП-шна штука на кшталт Haskell. Тож тут одразу +1 до приємності.
У Janet також є фішка з підтримкою PEG, якою вони надихнулися зокрема з REBOL/Red. Для задачі воно було непотрібно — можна було тупо працювати з charʼами, але мені кортіло спробувати, тож я запарсив файл отакою граматикою:
Через наявність PEG у Janet немає підтримки регулярних виразів, бо в принципі вони непотрібні. Я не певен, чи це добре: все-таки інколи фігачнути маленький regexp швидше й легше, але ок. Іншим мінусом Janetʼівського
Із загальних приємностей Janet: є fibers, threads, channels, streams тощо — все, що потрібно, є. Також доволі розвинена стандартна бібліотека, менеджер пакетів, FFI взаємодії з сішним кодом.
До синтаксису звикаєш значно швидше, ніж здалося на початку. А ось до назв функцій трохи повільніше. Мій чинний код стопудєй максимально далекий від ідіоматичного і ще далі від оптимального, але я отримав задоволення, доки писав!
Другу задачу, яка фактично єускладненою версією гри Sokoban , я так і не доробив трохи. Коли починав, то прийняв не найкращі рішення щодо структур даних, потім нахуєвертив зверху, задобався з цим боротися, та й загалом дуже втомився. На цьому мій Advent of Code завершився.
Я ще згодом напишу підсумки про саму подію, а зараз можу впевнено сказати, що Janet — це одна з моїх нових улюблених мов!
Ліспи з їхньою купою варіацій і REBOL з наразі єдиним нащадком Red — це D&D
Я вже намагався зробити підхід до Clojure десять років тому, але вочевидь був неготовий ментально. Зараз же зробив другу спробу і… не зміг себе пересилити: дивлюся в цей короткий лаконічний код, а подумки бачу, як JVM нестямно жере памʼять мого компа, і уявляю, як мені доведеться йти крізь ієрархію тек, щоб нарешті знайти там свій єдиний файл з кодом.
На щастя з одного зі стрімів пана Соловйова я дізнався про існування мови Janet
На початку було дуже складно, зокрема через відсутність навичок роботи з реплом та редагування лісп-коду. «Кляті дужки!»
Одним із сюрпризів став той факт, що на ліспі взагалі-то можна (і дуже легко) писати імперативно. Чогось був переконаний, що це чисто ФП-шна штука на кшталт Haskell. Тож тут одразу +1 до приємності.
У Janet також є фішка з підтримкою PEG, якою вони надихнулися зокрема з REBOL/Red. Для задачі воно було непотрібно — можна було тупо працювати з charʼами, але мені кортіло спробувати, тож я запарсив файл отакою граматикою:
(defn parse-input [input]
(def grammar
'{:wall (/ "#" :w)
:empty (/ "." :e)
:box (/ "O" :b)
:robot (/ (* (line :row) (column :col) "@") :r)
:cell (+ :wall :empty :box :robot)
:line (* (group (some :cell)) "\n")
:grid (group (some :line))
:up (/ "^" :up)
:down (/ "v" :down)
:left (/ "<" :left)
:right (/ ">" :right)
:direction (+ :up :down :left :right (? "\n"))
:moves (group (some :direction))
:main (*
:grid (-> :row) (-> :col)
"\n"
:moves)})
(let [[grid row col moves] (peg/match grammar input)]
[[(dec row) (dec col)] grid moves]))
Через наявність PEG у Janet немає підтримки регулярних виразів, бо в принципі вони непотрібні. Я не певен, чи це добре: все-таки інколи фігачнути маленький regexp швидше й легше, але ок. Іншим мінусом Janetʼівського
peg/match
є той факт, що вона працює тільки з рядками на вході. Якщо порівнювати з ріболівським parse
, це сильний недолік, адже там вони прям блоки коду (а код — це дані) парсять тим же самим парсом. Це логічніше, бо яка різниця, що ви отримуєте на вхід: список текстових літер чи список кейвордів? Через це нові діалекти в REBOL робити легше й приємніше.Із загальних приємностей Janet: є fibers, threads, channels, streams тощо — все, що потрібно, є. Також доволі розвинена стандартна бібліотека, менеджер пакетів, FFI взаємодії з сішним кодом.
До синтаксису звикаєш значно швидше, ніж здалося на початку. А ось до назв функцій трохи повільніше. Мій чинний код стопудєй максимально далекий від ідіоматичного і ще далі від оптимального, але я отримав задоволення, доки писав!
Другу задачу, яка фактично є
Я ще згодом напишу підсумки про саму подію, а зараз можу впевнено сказати, що Janet — це одна з моїх нових улюблених мов!
Please open Telegram to view this post
VIEW IN TELEGRAM
Трохи підібʼю підсумки Advent of Code, який для нормальних людей закінчився три дні тому, а для мене ще раніше. Сьогодні з трошечки більш технічного погляду, а завтра — з більш філософського.
Я брав участь вперше, і мені дуже сподобалося! Взагалі-то я алгоритмічні задачки не дуже люблю. Алгоритми — мій слабший за інші бік, і так було завжди. У роботі мені вистачало базових знань відмінностей між стандартними контейнерами, а основні рішення були радше архітектурними, ніж алгоритмічними. Ті ж BFS/DFS я не писав власноруч років 15. А зараз вже багато років я взагалі не отримую грошей безпосередньо за написання коду, бо давно посідаю роль майстра на всі руки (technical product manager, що б це не значило).
Тим не менш, я спробував розвʼязати задачі в перший день, у мене вийшло, а далі вже складно було відірватися. Разом їх аж 50 штук — по дві на кожен день з початку грудня й до Різдва. З них я отримав 34⭐️ , що вважаю непоганим результатом.
Прикол Advent of Code в тому, що він буде для вас настільки складний, наскільки складним ви самі хочете його зробити. Планка дуже низька, але ніхто не забороняє вам поставити особисті цілі: робити максимально короткі рішення, рішення з O(1) по памʼяті, розвʼязувати все в якомусь Excel чи COBOL — на ваш смак!
Якось так вийшло, що я вирішив писати щодня різними мовами програмування, багато з яких використовував уперше. Тому код мій здебільшого максимально тупий — аби працювало. Коли можна написати брутфорс, я його писав, хоча з другого тижня задачі пішли складніші, і повний перебір вже рідко давав результат.
Згодом мені неодноразово допоміг своїми порадами та поясненнями суперкрутий пан @ololocat, та й у принципі тримав мене в тонусі весь цей час, проте, до кінця я все-таки не дотягнув🥲
За весь період я писав на Haskell💻 , io 💻 , Nu 🆕 , Swift 🕊 , Python 💻 , Red 🔺 , C++ 💻 , Nim 👑 , Go 💻 , Elixir 💻 , Dart 💻 , SWI-Prolog 🦉 , Ada 💻 , Janet 👩🦰 , Crystal 🔮 , Lua 💻 (і Terra) і PHP 🕸 , отримавши багато корисного досвіду. (Не встиг спробувати: Pharo, Racket, F#, Uiua, Gleam, Zig).
Найцінніший «інсайт» (а точніше загальновідомий факт, про який писав ще Ніклаус Вірт у своїй книжці) — структури даних, які ви обираєте для своїх задач не менш важливі (я б сказав: значно важливіші) за алгоритми. За цей місяць я відчув це максимально яскраво: коли на початку обрав погану структуру даних, то рухатися далі вкрай боляче, а коли структура даних норм, то й алгоритм ллється наче пісня.
Якщо хтось захоче повторити мій шлях зі швидким опануванням якоїсь мови програмування(не треба) , то ось кілька порад:
• Якщо є офіційний туторіал від авторів мови, я читаю його. Вони зазавичай знають, з чого почати й у якій послідовності рухатися.
• Допомагає Learn X in Y minutes. Бували випадки, коли коротка стаття тут давала більше корисного, ніж офіційна документація.
• Дуже стає в пригоді ШІ-шка. Жодний з алгоритмів, який я просив її зробити, вона не зробила правильно, але в питаннях «як прочитати файл» або «як передати функцію у функцію» — неоціненний помічник на початку.
• В рідких випадках довелося читати книжки.
• Ну й краще мати попередній досвід зі схожими мовами😅 Але про це докладніше вже завтра.
Я брав участь вперше, і мені дуже сподобалося! Взагалі-то я алгоритмічні задачки не дуже люблю. Алгоритми — мій слабший за інші бік, і так було завжди. У роботі мені вистачало базових знань відмінностей між стандартними контейнерами, а основні рішення були радше архітектурними, ніж алгоритмічними. Ті ж BFS/DFS я не писав власноруч років 15. А зараз вже багато років я взагалі не отримую грошей безпосередньо за написання коду, бо давно посідаю роль майстра на всі руки (technical product manager, що б це не значило).
Тим не менш, я спробував розвʼязати задачі в перший день, у мене вийшло, а далі вже складно було відірватися. Разом їх аж 50 штук — по дві на кожен день з початку грудня й до Різдва. З них я отримав 34
Прикол Advent of Code в тому, що він буде для вас настільки складний, наскільки складним ви самі хочете його зробити. Планка дуже низька, але ніхто не забороняє вам поставити особисті цілі: робити максимально короткі рішення, рішення з O(1) по памʼяті, розвʼязувати все в якомусь Excel чи COBOL — на ваш смак!
Якось так вийшло, що я вирішив писати щодня різними мовами програмування, багато з яких використовував уперше. Тому код мій здебільшого максимально тупий — аби працювало. Коли можна написати брутфорс, я його писав, хоча з другого тижня задачі пішли складніші, і повний перебір вже рідко давав результат.
Згодом мені неодноразово допоміг своїми порадами та поясненнями суперкрутий пан @ololocat, та й у принципі тримав мене в тонусі весь цей час, проте, до кінця я все-таки не дотягнув
За весь період я писав на Haskell
Найцінніший «інсайт» (а точніше загальновідомий факт, про який писав ще Ніклаус Вірт у своїй книжці) — структури даних, які ви обираєте для своїх задач не менш важливі (я б сказав: значно важливіші) за алгоритми. За цей місяць я відчув це максимально яскраво: коли на початку обрав погану структуру даних, то рухатися далі вкрай боляче, а коли структура даних норм, то й алгоритм ллється наче пісня.
Якщо хтось захоче повторити мій шлях зі швидким опануванням якоїсь мови програмування
• Якщо є офіційний туторіал від авторів мови, я читаю його. Вони зазавичай знають, з чого почати й у якій послідовності рухатися.
• Допомагає Learn X in Y minutes. Бували випадки, коли коротка стаття тут давала більше корисного, ніж офіційна документація.
• Дуже стає в пригоді ШІ-шка. Жодний з алгоритмів, який я просив її зробити, вона не зробила правильно, але в питаннях «як прочитати файл» або «як передати функцію у функцію» — неоціненний помічник на початку.
• В рідких випадках довелося читати книжки.
• Ну й краще мати попередній досвід зі схожими мовами
Please open Telegram to view this post
VIEW IN TELEGRAM