Хотел было написать дополнение к предыдущему посту, но в результате набралось текста на отдельный. А все от того, что я начал играться с J. И он мне настолько нравится, что "приходится" делиться открытиями, хотя я обычно про программирование ничего не пишу.
Начал я с самого простого, с выражения, вычисляющего факториал числа. Оно удивительно компактно:
Несмотря на компактность, выражение состоит из трех функций. "i.5" генерирует вектор от 0 до 4. "1+" прибавляет к каждому элементу вектора единицу, чтобы получить 1..5. А "*/" перемножает элементы вектора. Само выражение -- 6 символов. Для читаемости можно было бы разбавить пробелами, но это необязательно. Наиболее компактное выражение на Скале, которое приходит на ум:
Возможно, на Хаскеле можно написать короче, но я с Хаскелем пока не знаком.
Если же захотеть завернуть выражение J в функцию, то все несколько сложнее и длиннее. Я долго мучался, пока не разобрался, как написать функцию факториала. Все дело в том, что разбор выражения в J право-ассициативный, поэтому a b x вычисляется как a(b(x)). Но если определять функцию f = (a b), то смысл меняется, хотя я до конца не понял, как. Вот короткий пример, прибавление к трем два, а потом умножение на два:
Как выражение в скобках получает из трех сорок, я пока не понимаю. Но для того, чтобы получить ожидаемый результат (10), надо явно указывать суперпозицию функций (оператор @):
Поэтому в случае с факториалом, функция определяется так:
Другой вариант -- использовать оператор инкремента ">:". Это не дает выигрыша в случае выражения, но немного короче для функции:
Интересная фича J -- "компиляция" или преобразование функций, которое разворачивает все примитивы в их простейшие выражения. Пример в предыдущем посте с числом "пи" треугольником, "f." -- оператор "компиляции":
Начал я с самого простого, с выражения, вычисляющего факториал числа. Оно удивительно компактно:
*/1+i.5 120
Несмотря на компактность, выражение состоит из трех функций. "i.5" генерирует вектор от 0 до 4. "1+" прибавляет к каждому элементу вектора единицу, чтобы получить 1..5. А "*/" перемножает элементы вектора. Само выражение -- 6 символов. Для читаемости можно было бы разбавить пробелами, но это необязательно. Наиболее компактное выражение на Скале, которое приходит на ум:
(1/:(1 to 5))(_*_)
Возможно, на Хаскеле можно написать короче, но я с Хаскелем пока не знаком.
Если же захотеть завернуть выражение J в функцию, то все несколько сложнее и длиннее. Я долго мучался, пока не разобрался, как написать функцию факториала. Все дело в том, что разбор выражения в J право-ассициативный, поэтому a b x вычисляется как a(b(x)). Но если определять функцию f = (a b), то смысл меняется, хотя я до конца не понял, как. Вот короткий пример, прибавление к трем два, а потом умножение на два:
2&* 2&+ 3 10 (2&* 2&+) 3 40
Как выражение в скобках получает из трех сорок, я пока не понимаю. Но для того, чтобы получить ожидаемый результат (10), надо явно указывать суперпозицию функций (оператор @):
(2&*@(2&+)) 3 10
Поэтому в случае с факториалом, функция определяется так:
f =: */@(1&+@i.) f 5 120
Другой вариант -- использовать оператор инкремента ">:". Это не дает выигрыша в случае выражения, но немного короче для функции:
f =: */@(>:@i.) f 5 120
Интересная фича J -- "компиляция" или преобразование функций, которое разворачивает все примитивы в их простейшие выражения. Пример в предыдущем посте с числом "пи" треугольником, "f." -- оператор "компиляции":
PiTree =: PiTree f. PiTree ([: - [: i. -@]) |."0 1 ([: #~ 1: + 2: * i.) ,/. [: ([: ": [: <.@o. 10x"_ ^ ]) [: <: [: *: ]После "компиляции" программы становятся максимально короткими, но плохо читаемыми. Симплекс-метод для задачи линейной оптимизации программируется несколькими понятными функциями. Но можно и свернуть в одну.
[-(({~"1<:@{:)-(i.@#@[=<:@{.@]))*/(({~<:@{.)%({~<@<:))Объяснение и хорошее введение в язык -- здесь