How to do it...

  1. For the HigherOrderFunctions.dproj project, the actual high-order functions are implemented in the HigherOrderFunctionsU.pas unit as generic class functions, as shown here:
type 
  HigherOrder = class sealed 
    class function Map<T>(InputArray: TArray<T>; 
MapFunction: TFunc<T, T>): TArray<T>; class function Reduce<T: record>(InputArray: TArray<T>; ReduceFunction: TFunc<T, T, T>; InitValue: T): T; class function Filter<T>(InputArray: TArray<T>; FilterFunction: TFunc<T, boolean>): TArray<T>; end;
  1. Let's analyze each of these functions. The Map function requires a list of T parameters as its input data structure, and an anonymous method that accepts and returns the same type of data T. For each element of the input data structure, the MapFunction is called, and another data list is built to contain all its results. This is the body of the Map function:
class function HigherOrder.Map<T>(InputArray: TArray<T>; 
    MapFunction: TFunc<T, T>): TArray<T>; 
var 
  I: Integer; 
begin 
  SetLength(Result, length(InputArray)); 
  for I := 0 to length(InputArray) - 1 do 
    Result[I] := MapFunction(InputArray[I]); 
end; 
  1. The main form uses the Map function in the following way:
procedure TMainForm.btnMapCapitalizeClick(Sender: TObject); 
var 
  InputData, OutputData: TArray<string>; 
begin 
  //let's generate some sample data 
  InputData := GetStringArrayOfData; 
   
  //call the map function on an array of string 
  OutputData := HigherOrder.Map<string>( 
    InputData, 
    function(Item: String): String 
    begin 
      //this is the "map" criteria that will be applied to each 
      //item to capitalize the first word in the item 
      Result := String(Item.Chars[0]).ToUpper + Item.Substring(1); 
    end); 
 
  //fill the related listbox with the results 
  FillList(OutputData, lbMap.Items); 
end; 
  1. The Reduce function requires a list of T as its input data structure, and an anonymous method that accepts two parameters of type T and returns a value of type T. It can also be passed as a default for each element of the input data structure; ReduceFunction is called by passing the intermediate result calculated so far, and the current element of the list. After the last call, the result is returned to the caller function. This is the body of the Reduce function:
class function HigherOrder.Reduce<T>(InputArray: TArray<T>; 
  ReduceFunction: TFunc<T, T, T>; InitValue: T): T; 
var 
  I: T; 
begin 
  Result := InitValue; 
  for I in InputArray do 
  begin 
    Result := ReduceFunction(Result, I); 
  end; 
end; 
  1. The main form uses the Reduce function in the following way:
procedure TMainForm.btnReduceSumClick(Sender: TObject); 
var 
  InputData: TArray<Integer>; 
  OutputData: Integer; 
begin 
  InputData := GetIntArrayOfData; 
  //sum the input data using as starting value 0 
  OutputData := HigherOrder.Reduce<Integer>(InputData, 
    function(Item1, Item2: Integer): Integer 
    begin 
      Result := Item1 + Item2; 
    end, 0); 
  lbReduce.Items.Add('SUM: ' + OutputData.ToString); 
end; 

  1. The last implemented function is Filter. The Filter function requires a list of T as its input data structure, and an anonymous method that accepts a single parameter of type T and returns a Boolean value. This anonymous method represents the filter criteria that will be applied to the input data. For each element of the input data structure, the FilterFunction is called; and if it returns true, then the current element will be in the returning list, but not otherwise. After the last call, the filtered list is returned to the caller function. Here is the body of the FilterFunction:
class function HigherOrder.Filter<T>(InputArray: TArray<T>; 
    FilterFunction: TFunc<T, boolean>): TArray<T>; 
var 
  I: Integer; 
  List: TList<T>; 
begin 
  List := TList<T>.Create; 
  try 
    for I := 0 to length(InputArray) - 1 do 
      if FilterFunction(InputArray[I]) then 
        List.Add(InputArray[I]); 
    Result := List.ToArray; 
  finally 
    List.Free; 
  end; 
end; 
  1. The main form uses the Filter function to filter only even numbers. The code is as follows:
procedure TMainForm.btnFilterEvenClick(Sender: TObject); 
var 
  InputData, OutputData: TArray<Integer>; 
begin 
  InputData := GetIntArrayOfData; 
  OutputData := HigherOrder.Filter<Integer>(InputData, 
    function(Item: Integer): boolean 
    begin 
      Result := Item mod 2 = 0; //gets only the even numbers 
    end); 
  FillList(OutputData, lbFilter.Items); 
end; 

In the recipe's code, there are other utilization samples related to higher-order functions.