Баннеры

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

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

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

яндекс

Новости

26.11.24. Вышла версия PascalABC.NET 3.10.1. Основное: новая эффективная реализация встроенных множеств set of T.

27.08.24. Вышла версия PascalABC.NET 3.10.0. Список изменений - здесь.

01.02.24. На ресурсе Stepik в ознакомительном режиме открыт курс "PascalАВС.NЕТ: продвинутый уровень".

10.07.23. Вышел релиз версии PascalABC.NET 3.9.0. Нововведения описаны здесь.

20.05.23. На странице https://pascalabc.net/stepikcourse опубликованы новые курсы по PascalABC.NET от центра олимпиадного программирования DL Club.