Существует интерактивная (и немного улучшенная) версия этой статьи на сайте wordsandbuttons.online: http://wordsandbuttons.online/apl_deserve_its_renaissance_too.html
Это Игра жизни в APL: life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}
Я знаю я знаю. Я должен был начать с введения, но разве оно не достаточно хорошо представляется? Вы сами можете убедиться, что он в конечном итоге лаконичен, выразителен и полностью чужд всем распространенным компьютерным языкам.
На самом деле он вообще не возник как компьютерный язык. Это было предложено в качестве лучшей записи для тензорной алгебры математиком из Гарварда Кеннетом Э. Айверсоном. Он должен был быть написан от руки на доске для передачи математических идей от одного человека к другому.
Но из-за своей формальности передача идей от людей к компьютерам оказалась на удивление хорошей. Он был превращен в компьютерный язык в начале 60-х, и эти странные символы, такие как⊖
или ⌽
, вообще не были проблемой, потому что у каждого производителя в то время и так был свой собственный набор символов. ASCII еще даже не ратифицировали.
Его популярность росла в последующие годы, достигнув пика в 70-х годах, а затем пошла на убыль с появлением персональных компьютеров на базе BASIC и платформы UNIX на основе C. Он все еще используется в некоторых нишах, например, в финансовом секторе, а это означает, что люди фактически зарабатывают деньги с помощью APL и по сей день. Но что удивительно, самый первый портативный компьютер от IBM - IBM 5100 «пакет интерактивных персональных вычислений весом 50 фунтов» - появился на 6 лет раньше IBM PC и с APL на борту.
Секрет популярности APL прост: изучение всех инопланетных символов - это единовременное вложение, а выразительность - влияние, которое вы, как программист, имеете на вычисления, - на всю жизнь.
И вообще выучить язык не так уж и сложно. Вы можете не поверить в это, но это один из самых простых языков на свете. Здесь позвольте мне показать вам, как работает Игра Жизни.
Левая стрелка - это функция присваивания, а скобки отмечают тело функции. Итак, это: life←{...}
это просто определение функции.
Аргументы функции APL являются неявными, то есть вам не нужно указывать имя для каждого аргумента, вы просто знаете по соглашению, что левый аргумент всегда ⍺, а правый. Разве это не означает, что функции APL принимают не более двух аргументов? Не совсем. Если вы хотите вызвать C-подобную функцию, например: foo(x, y, z)
, в терминах APL вы просто передаете кортеж из 3 значений. Но он все еще один ⍵.
Давайте проведем нашу жизнь с некоторым вкладом. Пусть будет планировщик. Мы будем использовать ⍴
функцию, чтобы сформировать матрицу из линейного массива.
in ← 5 5 ⍴ 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 in
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0
Запуск life for in приведет к следующему:
life in
0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 0 1 0 0
Планировщик переехал!
В APL то, что мы называем операторами, тоже являются функциями. Такие вещи, как +
, -
, *
и т. Д. Функции выполняются одновременно справа налево. Нет приоритета, все функции равны.
Первой функцией тела life
будет enclose: ⊂
. Что он делает - он превращает входные данные матрицы 5x5 в скаляр, содержащий матрицу 5x5.
⊂ in
┌─────────┐ │0 0 0 0 0│ │0 0 1 0 0│ │0 0 0 1 0│ │0 1 1 1 0│ │0 0 0 0 0│ └─────────┘
Следующий - немного сложнее. Следующая функция - повернуть: ⌽
. Он вращает массив по заданному индексу.
1 ⌽ 1 2 3
2 3 1
0 ⌽ 1 2 3
1 2 3
¯1 ⌽ 1 2 3
3 1 2
Но это не происходит само по себе. Он сам по себе является аргументом для оператора внешнего произведения: ∘.
(в функциях APL, которые принимают функции в качестве аргументов, они называются операторами).
И вместе они делают это:
¯1 0 1∘.⌽⊂in
┌─────────┬─────────┬─────────┐ │0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│ │0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│ │0 0 0 0 1│0 0 0 1 0│0 0 1 0 0│ │0 0 1 1 1│0 1 1 1 0│1 1 1 0 0│ │0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│ └─────────┴─────────┴─────────┘
Следующая функция также связана с оператором. Это сначала повернуть: ⊖
. Он работает почти так же, как rotate, но вращает вложенный массив вокруг первого уровня «вложенности».
1 ⊖ in
0 0 1 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0
0 ⊖ in
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0
¯1 ⊖ in
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 1 1 0
С оператором внешнего продукта и нашим предыдущим результатом это выглядит так:
¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
┌─────────┬─────────┬─────────┐ │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│0 0 1 0 0│0 1 0 0 0│ │0 0 0 0 1│0 0 0 1 0│0 0 1 0 0│ │0 0 1 1 1│0 1 1 1 0│1 1 1 0 0│ ├─────────┼─────────┼─────────┤ │0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│ │0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│ │0 0 0 0 1│0 0 0 1 0│0 0 1 0 0│ │0 0 1 1 1│0 1 1 1 0│1 1 1 0 0│ │0 0 0 0 0│0 0 0 0 0│0 0 0 0 0│ ├─────────┼─────────┼─────────┤ │0 0 0 1 0│0 0 1 0 0│0 1 0 0 0│ │0 0 0 0 1│0 0 0 1 0│0 0 1 0 0│ │0 0 1 1 1│0 1 1 1 0│1 1 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│ └─────────┴─────────┴─────────┘
Следующая функция называется ravel: ,
, и она действительно похожа на кому. Что он делает, он превращает вложенный массив в одномерный.
, in
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0
Он не использует скаляр, поэтому, применяя его к нашей матрице скаляров, содержащей матрицы, он будет представлять собой массив из 9 закрытых матриц.
Следующая - снова пара оператора и функции. Оператор уменьшить: /
и функция плюс: +
. Как вы могли догадаться, это уменьшает суммирование всех матриц в массиве:
+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
┌─────────┐ │0 1 1 1 0│ │0 1 2 2 1│ │1 3 5 4 2│ │1 2 4 3 2│ │1 2 3 2 1│ └─────────┘
Сравнение операторов: =
производит матрицы из 0 и 1 в зависимости от того, равен ли каждый элемент в правом аргументе каждому аргументу в левом аргументе. В нашем случае это приведет к:
3 4 = +/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
┌─────────┬─────────┐ │0 0 0 0 0│0 0 0 0 0│ │0 0 0 0 0│0 0 0 0 0│ │0 1 0 0 0│0 0 0 1 0│ │0 0 0 1 0│0 0 1 0 0│ │0 0 1 0 0│0 0 0 0 0│ └─────────┴─────────┘
Тогда есть логическая часть. Функции или: ∨
и и: ∧
используются с оператором внутренний продукт .
. С нашим вводом это приводит к закрытой матрице, где каждый 1 элемент исходного ввода приводит к тому, что 1 имеет только 2 исходных соседа, но также каждый элемент вообще приводит к тому, что 1 имеет 3 соседа, независимо от того, был ли он установлен на 1 в исходном вводе или нет.
Это в основном правила Игры в жизнь.
1 in∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
┌─────────┐ │0 0 0 0 0│ │0 0 0 0 0│ │0 1 0 1 0│ │0 0 1 1 0│ │0 0 1 0 0│ └─────────┘
И последняя функция mix ↑
здесь просто удаляет вложенность.
↑1 in∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂in
0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 0 0 0 1 0 0
Совсем не чуждо сейчас, не так ли?
Но почему он все-таки заслуживает возрождения?
Он был разработан для написания от руки. Однако в течение десятилетий он вводился только с клавиатуры. И странная, не та, которую можно просто купить в любом магазине. Однако у APL есть свои ASCII-дружественные потомки, унаследовавшие его выразительность и лаконичность, но, честно говоря, все они уродливы, за пределами возможности общественного успеха. Дело не в том, что APL чужды компьютерам, просто компьютеры были чужды APL на долгое время.
Но теперь, с развитием сенсорных интерфейсов и оптического распознавания символов, у него может появиться второй шанс. Лично я предпочел бы рисовать несколько строк символов APL на планшете, чем набирать сотни строк на Python с виртуальной клавиатурой.
Хакерский полдень - это то, с чего хакеры начинают свои дни. Мы часть семьи @AMI. Сейчас мы принимаем заявки и рады обсуждать рекламные и спонсорские возможности.
Если вам понравился этот рассказ, мы рекомендуем прочитать наши Последние технические истории и Современные технические истории. До следующего раза не воспринимайте реалии мира как должное!