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

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

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

Новости

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

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

31.12.15. Версия 3.0.0.1128. Реализованы обобщенные методы расширения для операций

22.12.15. Версия 3.0.0.1116. Реализован новый синтаксис extension-методов

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

// Пузырьковая сортировка с флагом
// Уровень сложности: 1
procedure BubbleSort(a: array of integer);
begin
  var i := a.Length - 1;
  var q: boolean;
  repeat
    q := true;
    for var j := 0 to i - 1 do
      if a[j + 1] < a[j] then
      begin
        Swap(a[j + 1], a[j]);
        q := false;
      end;
    i -= 1;
  until q;
end;

const N = 10;

begin
  var a := ArrRandom(N);
  writeln('Исходный массив: ');
  a.Println;
  BubbleSort(a);
  writeln('Отсортированный массив: ');
  a.Println;
end.