1

Тема: APL

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

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

{⍵∘.×⍵}1+⍳10
Прихований текст
(якщо замість половини символів відобразились квадратики, поставте собі нормальні шрифти — FreeMono підійде).

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

life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}
py -3 -m pip install git+https://github.com/snoack/python-goto
Подякували: 0xDADA11C71

2

Re: APL

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

МАКЕ ЦКЯАІИЕ БЯЕАТ АБАІИ

3 Востаннє редагувалося P.Y. (19.11.2016 23: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
py -3 -m pip install git+https://github.com/snoack/python-goto
Подякували: quez, ReAl, 0xDADA11C73

4

Re: APL

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

5

Re: APL

Новий?  :o

МАКЕ ЦКЯАІИЕ БЯЕАТ АБАІИ
Подякували: ReAl1

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

Re: APL

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

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");

7

Re: APL

Torbins написав:

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

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

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

py -3 -m pip install git+https://github.com/snoack/python-goto

8 Востаннє редагувалося P.Y. (26.11.2016 23: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
py -3 -m pip install git+https://github.com/snoack/python-goto
Подякували: 0xDADA11C7, ReAl2

9

Re: APL

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

Говорила баба діду: «Я поїду к Білодіду, Ізучу двомовну мову І вернусь обратно знову». А дід бабі: «Не *изди, К Білодіду нєт їзди, — Туди не ходять поїзди»

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

Re: APL

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

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

py -3 -m pip install git+https://github.com/snoack/python-goto
Подякували: 0xDADA11C7, /KIT\2

11 Востаннє редагувалося /KIT\ (03.12.2016 14:17:40)

Re: APL

P.Y. написав:

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

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

...

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

Re: APL

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

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

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

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

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

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

py -3 -m pip install git+https://github.com/snoack/python-goto

13

Re: APL

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

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

py -3 -m pip install git+https://github.com/snoack/python-goto
Подякували: 0xDADA11C71

14

Re: APL

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

Говорила баба діду: «Я поїду к Білодіду, Ізучу двомовну мову І вернусь обратно знову». А дід бабі: «Не *изди, К Білодіду нєт їзди, — Туди не ходять поїзди»