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

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

С точки зрения функционального программирования 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 полноценной реализации лямбда-функций.

Новости

13.07.19. Опубликована презентация Новые возможности PascalABC.NET 3.5 (2015-2019 гг).

09.02.19. Опубликованы книги Валерия Рубанцева Занимательные проекты на Паскале и PascalABC.NET. Программирование графики на примерах.

02.02.19. Опубликована наиболее известная из книг Валерия Рубанцева Занимательные уроки с Паскалем и книга Графика для компьютерных игр.

30.01.19. Опубликованы две книги Валерия Рубанцева из серии Развивающее программирование: Решение задач на языке Паскаль и Увлекательная математика с Паскалем.

28.01.19. По согласованию с автором открыта страница Валерия Рубанцева с книгами о PascalABC.NET. Опубликована книга Функциональное программирование на PascalABC.NET.

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

// Функция факториала
// Уровень сложности: 0

function Fact(n: integer): BigInteger;
begin
  Result := 1;
  for var i:=2 to n do
    Result *= i;
end;

begin
   writeln(Fact(100));
end.