Зарисовки в функциональном стиле

Просмотров: 7222

С точки зрения функционального программирования PascalABC.NET - плохой язык. Однако, некоторые идеи, тем не менее, могут быть реализованы в функциональном стиле. На них и остановимся.

В качестве объекта изложения возьмем алгоритм получения следующего числа в цепочке в задаче 74 из проекта Эйлера.

Каждый следующий член цепочки получается из предыдущего как сумма факториалов его цифр:

1!+6!+9! =363601

169 → 363601 → 1454 → 169

Вначале разобьём целое на массив цифр. Сделаем это с помощью метода расширения для integer. Алгоритм - безобразно неоптимальный:

type 
  IntArray = array of integer;
  
function integer.ToDigits(): IntArray;
begin
  var s := Self.ToString();
  SetLength(Result,s.Length);
  for var i := 1 to s.Length do
    Result[i-1] := OrdUnicode(s[i])-OrdUnicode('0');
end;

Для массива целых составим метод расширения, вычисляющий сумму элементов:

function IntArray.Sum(): integer;
begin
  Result := 0;
  for var i := 0 to Self.Length-1 do
    Result += Self[i];
end;

Для вывода массива целых на экран расширим его тип методом Print:

procedure IntArray.Print;
begin
  for var i := 0 to Self.Length-1 do
    write(Self[i],' ');
end;

Наконец, напишем метод расширения для преобразования массива целых в массив целых, воздействуя на каждый элемент функцией, передаваемой в качестве параметра:

type 
  IntFun = function (i: integer): integer;
 
function IntArray.Map(f: IntFun): IntArray;
begin
  SetLength(Result,Self.Length);
  for var i := 0 to Self.Length-1 do
    Result[i] := f(Self[i]);
end;

Проверяем:

function fact(n: integer): integer;
begin
  Result := 1;
  for var i:=1 to n do
    Result *= i;
end;
 
begin
  var i := 145;
  writeln(i.ToDigits().Map(fact).Sum());
end. 

Выводится 145 (145=1!+4!+5!)

Выведем 4 члена цепочки, начинающейся со 169. Для этого реализуем функцию перехода к следующему элементу:

function Next(i: integer): integer;
begin
  Result := i.ToDigits().Map(fact).Sum();
end;

Наконец, реализуем для данного целого функцию получения цепочки целых с указанной функцией перехода к следующему элементу:

function integer.Take(f: IntFun; n: integer): IntArray;
begin
  SetLength(Result,n);
  Result[0] := Self;
  for var i:=1 to n-1 do
    Result[i] := f(Result[i-1]);
end;

Пробуем:

begin
  var i := 169;
  i.Take(Next,4).Print();
end.

Получаем цепочку из 4 элементов:

169 363601 1454 169 

Полный код программы - здесь.

К сожалению, код не столь красив ввиду отсутствия в PascalABC.NET полноценной реализации лямбда-функций.

Новости

30.08.17. Вышла версия 3.3. Новые модули Graph3D и GraphWPF

19.01.17. Добавлена операция безопасного среза: a?[-1:5:2]

29.08.16. Вышла версия 3.2. Реализован оператор yield.

12.02.16. Вышла версия 3.1. Добавлены кортежи в стиле (a,b) и кортежное присваивание (a,b) := (b,a)

Случайная программа

// Числа Фибоначчи
// Уровень сложности: 0
const n = 25;

begin
  var a := 1;
  var b := 1;
  Print(a,b);
  for var i := 3 to n do
  begin
    var c := a + b;
    Print(c);
    a := b;
    b := c;
  end;
end.