1

Тема: APL

Хтось іще тут пробував освоїти цю штуку?

Поки що не дуже уявляю, де цю мову реально можна застосувати і хто нею користується. Більшість відштовхує специфічний набір символів, використовуваних для написання програм (тобто, замість ключових слів з латинських літер — додаткові символи), деякої незручності також додає незвичний порядок виконання операцій і незвична логіка мови вцілому. З іншого боку, не в останню чергу завдяки цим особливостям мова дуже компактна. Наприклад, код, що виведе таблицю Піфагора, виглядатиме так:

{⍵∘.×⍵}1+⍳10
Прихований текст

(якщо замість половини символів відобразились квадратики, поставте собі нормальні шрифти — FreeMono підійде).

Як бачите, вся програма складається з 12 знаків і абсолютно незрозуміла без попередньої підготовки.
Але, з іншого боку, можливість написати, наприклад, «гру життя» одним рядком — це  крутіше, ніж регекси... Власне, ось:

life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}
Подякували: 0xDADA11C71

2

Re: APL

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

3 Востаннє редагувалося P.Y. (19.11.2016 22:44:45)

Re: APL

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

Перший приклад написав я сам. Як це працює:
⍳10 — аналог пітонівського range(10) — генерує масив чисел від 0 до 9.
Але нам треба масив від 1 до 10, тому робимо 1+⍳10 — одиниця додається до кожного елемента.
Далі, те, що у фігурних дужках — анонімна функція з одним аргументом ⍵ (можливі також функції з двома аргументами, які позначаються ⍺ й ⍵).
Оператор множення × може приймати як числа, так і масиви (напр., 1 2 3×1 2 3 дає результат 1 4 9, а 10×1 2 3 дає результат 10 20 30). Тобто, якби ми записали {⍵×⍵}1+⍳10, це дало б список квадратів чисел: 1 4 9 ... 100, що незовсім те, що нам треба.
Тому перед знаком множення ставимо модифікатор ∘. — тоді кожен елемент правого операнда буде перемножено з кожним елементом лівого операнда, результат повернеться в вигляді двовимірного масива:

      {⍵∘.×⍵}1+⍳10
 1  2  3  4  5  6  7  8  9  10
 2  4  6  8 10 12 14 16 18  20
 3  6  9 12 15 18 21 24 27  30
 4  8 12 16 20 24 28 32 36  40
 5 10 15 20 25 30 35 40 45  50
 6 12 18 24 30 36 42 48 54  60
 7 14 21 28 35 42 49 56 63  70
 8 16 24 32 40 48 56 64 72  80
 9 18 27 36 45 54 63 72 81  90
10 20 30 40 50 60 70 80 90 100
Подякували: quez, ReAl, 0xDADA11C73

4

Re: APL

Ух ти, хтось винайшов новий Брейн фак :)

5

Re: APL

Новий?  :o

Подякували: ReAl1

6 Востаннє редагувалося ReAl (21.11.2016 12:59:24)

Re: APL

APL трохи молодший за FORTRAN, трохи старший за С і тим більше за новомодні штучки на зразок C++ і тим більше brainfuck

Подякували: P.Y.1

7

Re: APL

Torbins написав:

Ух ти, хтось винайшов новий Брейн фак :)

Скоріш, щось назразок регексів з ієрогліфами, призначених для роботи з багатовимірними масивами.

Як написали вище, мова не нова. Про APL я вперше дізнався з книжки 1979 року видання, яку мама помилково купила замість книжки з PL/1.

8 Востаннє редагувалося P.Y. (26.11.2016 22:37:33)

Re: APL

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

mylife←{((⍵×9)+⊃+/+/¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵)∊12 13 3}

Спробую пояснити, як я це зробив.
Перше, що треба знати про APL: усі оператори (які можуть унарними чи бінарними) мають однаковий пріоритет і є правоасоціативними — тому 2×2+2 = 2×(2+2) = 8, а 1-2-3 = 1-(2-3) = 1-¯1 = 2. Це пояснить, чому в одних випадках дужки потрібні, а в інших без них можна обійтися. Друге: всі дані бувають двох основних типів: скаляри і масиви. Одновимірні масиви чисел задаються серією чисел, розділених, пробілами. Дво- та багатовимірні можна зробити з одновимірних оператором ⍴, напр.:

      2 3⍴1 2 3 4 5 6
1 2 3
4 5 6

У грі ми будемо задавати ігрове поле двовимірним масивом нулів та одиниць:

      map←4 4⍴0 0 0 0 1 1 1 0 0 0 0 0
0 0 0 0
1 1 1 0
0 0 0 0
0 0 0 0

(якщо праворуч від ⍴ менше чисел, ніж треба для заданої розмірності, він переходить до початку вхідного масиву і йде по новому кругу, тому все ОК).

Щоб знайти положення наступного ходу, нам треба порахувати кількість одиниць у сусідніх клітинках. За правилами, заповнена клітинка, біля якої є 2 або 3 сусіди, лишається заповненою, якщо більше або менше — звільняється; якщо біля порожньої клітинки є рівно 3 заповнені, вона також стає заповненою. Отже, нам треба знайти суму всіх сусідніх клітинок з особливим урахуванням поточної.

В APL є оператори прокрутки ⌽ та ⊖:

      1⌽map
0 0 0 0
1 1 0 1
0 0 0 0
0 0 0 0
      ¯1⌽map
0 0 0 0
0 1 1 1
0 0 0 0
0 0 0 0
      1⊖map
1 1 1 0
0 0 0 0
0 0 0 0
0 0 0 0
      ¯1⊖map
0 0 0 0
0 0 0 0
1 1 1 0
0 0 0 0

Нам треба зробити всі можливі комбінації зсувів на -1, 0, 1 по горизонталі та вертикалі. Проблема в тому, що для адекватної роботи операторів прокрутки ліворуч має бути число, а не масив. Можна застосувати модифікатор ∘. — але й він робить незовсім те, що нам треба.

      ¯1 0 1∘.⌽map
0 0 0 0
1 1 1 0
0 0 0 0
0 0 0 0

0 0 0 0
1 1 1 0
0 0 0 0
0 0 0 0

0 0 0 0
1 1 1 0
0 0 0 0
0 0 0 0

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

Щоб обійти цю проблему, можна загорнути масив у скалярний контейнер оператором ⊂

      ¯1 0 1∘.⌽⊂map
 0 0 0 0   0 0 0 0   0 0 0 0
 0 1 1 1   1 1 1 0   1 1 0 1
 0 0 0 0   0 0 0 0   0 0 0 0
 0 0 0 0   0 0 0 0   0 0 0 0

Працює!
Контейнер є для зовнішнього світу скаляром, тому модифікатор комбінаційного добутку не розбирає його на елементи, всередині ж він лишається масивом і при обчисленні результату обробляється як масив. При виводі масив у контейнері та без виглядають ідентично, але їх можна відрізнити за розмірністю (унарний оператор ⍴):

      ⍴map
4 4
      ⍴⊂map
(пустий рядок)

Зробімо тепер усі можливі комбінації прокрутки:

      {¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}map
 0 0 0 0   0 0 0 0   0 0 0 0
 0 0 0 0   0 0 0 0   0 0 0 0
 0 1 1 1   1 1 1 0   1 1 0 1
 0 0 0 0   0 0 0 0   0 0 0 0
 0 0 0 0   0 0 0 0   0 0 0 0
 0 1 1 1   1 1 1 0   1 1 0 1
 0 0 0 0   0 0 0 0   0 0 0 0
 0 0 0 0   0 0 0 0   0 0 0 0
 0 1 1 1   1 1 1 0   1 1 0 1
 0 0 0 0   0 0 0 0   0 0 0 0
 0 0 0 0   0 0 0 0   0 0 0 0
 0 0 0 0   0 0 0 0   0 0 0 0

Ми отримали двовимірний масив контейнерів двовимірних масивів. Ця частина коду присутня в обох реалізаціях — моїй і з вікіпедії. Далі треба зробити з них один масив із сумами сусідніх клітинок:

      {+/+/¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}map
2 3 2 2
2 3 2 2
2 3 2 2
0 0 0 0

Оператор + виконує додавання, а / поруч із ним означає не ділення (для якого використовується ÷), а редукцію — тобто, +/1 2 3 4 означатиме те ж саме, що 1+2+3+4. Арифметичні операції вміють працювати з двома числами, числом і масивом, двома масивами однакової розмірності. У даному випадку, між собою додаються скалярні контейнери з двовимірними масивами однакової розмірності, а результатом є скалярний контейнер, де кожен елемент являє собою суму елементів вхідних масивів у відповідних позиціях. Редукція відбувається лише в одному вимірі, тому її треба застосувати двічі. В результаті, ми отримуємо контейнер з масивом, кожен елемент якого містить суму сусідніх клітинок (включаючи поточну) початкового ігрового поля.

Після цього масив виймається з контейнера оператором ⊃, до нього додається початковий масив, кожен елемент якого помножено на 9:

      {(9×⍵)+⊃+/+/¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}map
 2  3  2 2
11 12 11 2
 2  3  2 2
 0  0  0 0

Таким чином, порожня клітинка з 0, 1,…, 8 сусідів дасть результат 0, 1,…, 8, а заповнена з 0, 1,…, 8 сусідів — 10, 11,…, 18 відповідно. Далі нам треба лише вписати одинички в порожні клітинки з трьома сусідами й заповнені з двома чи трьома сусідами, а решту заповнити нулями (оператор ∊ — аналог pythonівського in, подібний символ використовується в схожій ролі в математичних формулах):

      {((9×⍵)+⊃+/+/¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵)∊3 12 13}map
0 1 0 0
0 1 0 0
0 1 0 0
0 0 0 0

Результат відповідає очікуваному. Можна оформити цей вираз як функцію й користуватись нею:

      mylife←{((9×⍵)+⊃+/+/¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵)∊3 12 13}
#procedure
      mylife map
0 1 0 0
0 1 0 0
0 1 0 0
0 0 0 0
      mylife mylife map
0 0 0 0
1 1 1 0
0 0 0 0
0 0 0 0
Подякували: 0xDADA11C7, ReAl2

9

Re: APL

Надзвичайно цікавий динозавр, схвалюю  *BRAVO*

Подякували: 221VOLT1

10 Востаннє редагувалося P.Y. (29.11.2016 14:58:18)

Re: APL

Хотілось би якось роздинозаврити його. Можливо, в ролі не самостійної мови, а чогось назразок бібліотеки регексів для матриць, додавши можливість передавати екзотичні символи транслітерацією чи мнемонікою. Такий шлях розвитку здається мені більш перспективним, ніж спроби зробити з APL мейнстрімну мову з попсовими ООП-фічурами.

Ще мені здається цікавою ідея застосувати APL для обробки графіки. Мову, орієнтовану на масиви, застосувати для обробки масивів пікселів — ніби звучить логічно? Писати шейдери на APL?..

Подякували: 0xDADA11C7, /KIT\, bunyk3

11 Востаннє редагувалося P.Y. (07.12.2016 14:55:45)

Re: APL

/KIT\ написав:
P.Y. написав:

Ще мені здається цікавою ідея застосувати APL для обробки графіки. Мову, орієнтовану на масиви, застосувати для обробки масивів пікселів — ніби звучить логічно? Писати шейдери на APL?..

А це тема! Але, чи буде шейдер на APL працювати швидше ніж інші?

Самому цікаво :)
По ідеї, залежить від конкретної реалізації. Якщо програма компілюється в добре оптимізовані інструкції графічного процесора, то цілком можливо. Плюс можлива оптимізація на рівні мови, що оперує з масивами — напр., неявне кешування масивів замість їх повторного обчислення¹. Але поки що мене більш цікавить складність такої задачі для кодера.

Розвідуючи проблему, ще нагуглив ось таке: https://github.com/melsman/apltail/ — не можу сказати, наскільки ця річ придатна для роботи з графікою, але ніби дозволяє писати код для обробки на граф. процесорі.

¹) Або, ще краще, маніпуляції з індексами замість фізичного перенесення даних — напр., при транспонуванні матриці.

12

Re: APL

Змайстрував клавіатурну розкладку, куди спробував увіпхнути всі символи, використовувані в APL. Більшість із них, звичайно ж, не використовуються в жодній відомій мені реалізації мови, і з назви важко сказати, для чого вони (але для чогось же в Юнікоді їх передбачили).

В багатьох реалізаціях мови середовище розробки обладнане засобами вводу екзотичних символів. Проте, не скрізь (напр., онлайн-версія NGN APL має вбудовану клавіатуру, в консольному ж варіанті використовуються встановлені на комп'ютері розкладки). Крім того, інколи виникає потреба використовувати ці символи окремо (напр., для пошуку в документації, або при вводі коду в звичайному редакторі).

Подякували: 0xDADA11C71

13

Re: APL

Я б на вашому місці курив би план, читав dark bit factory та майстрував інтерпретатор на асмі з графічними можливостями.

14

Re: APL

Насправді доходжу висновку, що жодна з реалізацій APL, які мені траплялися, мене не влаштовує.
Dyalog: наймейнстрімніше з усього, що є, але пропрієтарне й роздуте.
NARS2000: опенсорс із доволі цікавими мовними фічами, але обмеженим функціоналом (тільки GUI-консоль, ніякого скриптингу чи компіляції в самостійні виконувані файли; звісно, можна колупати код, але...).
GNU APL: опенсорс, що застряг у розвитку. Має все необхідне, щоб писати на ньому повноцінні програми, але в олдскульному стилі. Не для пещених професіоналів, що непритомніють від слова GOTO й не вміють писати код без ООП. Ну й власне APL-специфічні фічі в ньому також застрягли десь на рівні 80-х.
NGN APL: іграшковий опенсорний Dyalog-подібний діалект. Написаний на JS, працює в браузері чи ноді. Іграшковий — тобто, початково придатний лише для «погратися з синтаксисом». Проте, з деякими змінами (що відносно просто) може робити все, що може робити програма на JS (і ще, JS — отже, інтегрованість у браузери, що також плюс). Зараз потихеньку пиляю його, додаючи нові фічі (напр., роботу з файлами чи деякі відсутні оператори та функції) — коли доведу до більш-менш нестрашного стану, викладу свій форк на Ґітхабі.

Власне, правильний APL треба писати самому. Бо, хоча це одна з небагатьох мов, від яких мене пре, розвиток її відбувався абсолютно не в тому напрямку. Період розквіту APL — це десь приблизно 70-ті роки з модною в той час інтерактивністю (ті, хто вчились програмувати на ранніх бейсіках, мене зрозуміють), класичне середовище розробки являє собою термінал, що взаємодіє з розміщеними на сервері воркспейсами, де зберігаються змінні та функції (тобто, професійний APLщик має справу не з файлами на APL, а з чимось назразок БД, де зберігаються дані та код. На Dyalog'івських конференціях лише в останні роки почали говорити про такий прогресивний крок, як «файли початкового коду»). І ще, треба розуміти, що APL розвивався як мова бухгалтерів (що, з моєї точки зору, нагадує забивання цвяхів телескопом. Чи калейдоскопом), «справжнім програмістам» він був переважно нецікавий — і це теж наклало відбиток на його розвиток. Мова з крутезними засобами для роботи з масивами — але без літералів для багатовимірних масивів, та ще й обмежена однорядковим синтаксисом. Теоретично, придатна для роботи з графікою — але націлена на роботу переважно з float'ами, що ускладнює задачу написання оптимального коду для маніпуляцій з цілочисельними пікселями. Побітові and, or, xor? Ні, не чули (хоча ∨ та ∧ можуть приймати аргументи, більші за 1, вони з ними роблять не побітові дії, а шукають найменше спільне кратне та найбільший спільний дільник). ФП? Потужні засоби для конструювання нових функцій (гугліть atop, fork, train, плюс оператори в асортименті) — але незрозуміле положення функцій, які не є рівноправними об'єктами, як у JS чи ліспі (наприклад, ви не можете покласти функцію в масив. Утім, мабуть, APL узагалі не про те). Інтегрованість? Ні, я розумію, що багатьох лякають ці специфічні символи (що насправді дрібниця в часи панування UTF-8. Вас же не лякає, що більшість текстів на цьому форумі написано не на базовому ASCII?), але справжня проблема не в цьому, а в зручних усталених механізмах поєднати код на APL та якійсь мейнстрімній мові загального призначення (подібно до того, як ми зв'язуємо php та sql, js та регекси, python та c++...), щоб однорядкова дія на APL не вимагала кілометрового полотна для її вживлення в основну програму.

Подякували: NaharD, /KIT\, ReAl, 0xDADA11C7, leofun015

15

Re: APL

Хотілось би якось роздинозаврити його. Можливо, в ролі не самостійної мови, а чогось назразок бібліотеки регексів для матриць, додавши можливість передавати екзотичні символи транслітерацією чи мнемонікою. Такий шлях розвитку здається мені більш перспективним, ніж спроби зробити з APL мейнстрімну мову з попсовими ООП-фічурами.

2016.11.26 Карл! Ви вже рік розповідаєте про ваше бачення APL, а рядка коду для нової ралізації простої мови не написали. Ви чекаєте коли цим тредом зацікавиться СправжнійПрограміст™, витягне з вас бачення нової мови, оформить у вигляді ТЗ з правильними граматиками, напише принаймні інтерпретатора, біндінги для інтеграції у високорівневі мови програмування, а потім винайме вас на роботу для роботи саме з цією мовою програмування вашої мрії? Звісно, щоб та робота не зачіпала як прикладних завданнь, так і реалізації власне мови - наприклад, помічником голови комісії зі стандартизації мови.

Подякували: leofun011

16 Востаннє редагувалося bunyk (10.01.2018 14:05:12)

Re: APL

Бувають ще ASCII діалекти APL, які все ще компактні наприклад K і J.

17

Re: APL

Якийсь аналог ASCII-діалекту APL можна зробити навіть засобами пітона з NumPy (що має схоже призначення), визначивши скорочені імена функцій замість повних-багатослівних — але це трохи не те задоволення. Хоча, з іншого боку, якщо порівняти любий моєму серцю NGN-APL та NumPy, то пітонівська бібліотека з фортраном під капотом, імовірно, виграватиме за швидкодією перед реалізацією на базі ЈЅ. І треба мати на увазі, що хоча це все масиво-орієнтоване програмування, існують розбіжності між різними реалізаціями ціє парадигми: навіть деякі базові дії з масивами в одній мові прямих аналогів в іншій можуть не мати. Крім того, APL обріс функціональною складовою, а прямих аналогів APLівських операторів (функцій, що оперують функціями) Python+NumPy не надає — там своє пітонівське ФП, зроблене по-іншому. Ідея переписати свій APL-код на Python+NumPy у мене виникала, але, схоже, це поки що непідйомна задача.

А ще бачив ось таке: https://github.com/baruchel/apl — дуже сира реалізація APL на базі NumPy, з невеликим асортиментом реалізованих функцій і неповним синтаксисом.