DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Required Capabilities in Self-Navigating Vehicle-Processing Architectures
  • Process Mining Key Elements
  • Reversing an Array: An Exploration of Array Manipulation
  • Low Code/No Code Testing Approach for Salesforce Testing

Trending

  • Liquid Glass, Material 3, and a Lot of Plumbing
  • The Documentation Crisis Nobody Sees: Why AI Agents Are Breaking Faster Than Humans Can Document Them
  • Good Data, Bad Metric: A Mutation Testing Pattern for Analytics Engineering
  • Why Your Test Automation Is Always Behind the Code And the Architecture That Fixes It
  1. DZone
  2. Data Engineering
  3. Data
  4. Fun With Anonymous Methods: Using Higher-Order Functions in Delphi

Fun With Anonymous Methods: Using Higher-Order Functions in Delphi

We take a look at how developers can use Delphi (a dialect of Object Pascal) to write anonymous methods and higher-order functions.

By 
Richard Gall user avatar
Richard Gall
·
Jan. 07, 19 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
11.3K Views

Join the DZone community and get the full member experience.

Join For Free

Since 2009, the Delphi language (or better, its Object Pascal dialect) has supported anonymous methods.

What Is an Anonymous Method?

Not surprisingly, an anonymous method is a procedure or a function that does not have an associated name. An anonymous method treats a block of code just like a value so that it can be assigned to a variable, used as a parameter to a method, or returned by a function as its result value.

In addition, an anonymous method can refer to variables and bind values to them in the context scope in which the anonymous method is defined. Anonymous methods are similar to closures defined in other languages, such as JavaScript or C#. An anonymous method type is declared as a reference to a function:

type 
  TFuncOfString = reference to function(S: String): String;

Anonymous methods (or anonymous functions) are convenient for passing as an argument to a higher-order function. What's a higher-order function?

Wikipedia:

In mathematics and computer science, a higher-order function (also functional form, functional, or functor) is a function that does at least one of the following:

  • Takes one or more functions as an input.

  • Outputs a function.

All other functions are first-order functions.

We’ll now take a look at anonymous methods in a number of useful recipes, taken from the third edition of Delphi Cookbook written by Daniele Teti and Daniele Spinetti, published by Packt.

Getting Ready

In this recipe, you'll see how to use Delphi's anonymous methods with some of the most popular and useful higher-order functions:

  • Map: This is available in many functional programming languages. This takes as arguments a func function and a list of elements list, and returns a new list with func applied to each element of the list.

  • Reduce: This is also known as Fold. This requires a combining function, a starting point for a data structure, and possibly some default values to be used under certain conditions. The Reduce function proceeds to combine elements of the data structure using the injected function. This is used to perform operations on a set of values to get only one result (or a smaller set of values) that represents the reduction of that initial data. For example, the values 1, 2, and 3 can be reduced to the single value 6 using the SUM .

  • Filter: This requires a data structure and a filter condition. This returns all the elements in the structure that match the filter condition.

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 = classsealedclassfunction Map<T>(InputArray: TArray<T>; 
      MapFunction: TFunc<T, T>): TArray<T>; 
    classfunction Reduce<T: record>(InputArray: TArray<T>; 
      ReduceFunction: TFunc<T, T, T>; InitValue: T): T; 
    classfunction Filter<T>(InputArray: TArray<T>; 
      FilterFunction: TFunc<T, boolean>): TArray<T>; 
  End;

2. 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 Mapfunction:

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;

3.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;

4.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 Reducefunction:

classfunction HigherOrder.Reduce<T>(InputArray: TArray<T>; 
  ReduceFunction: TFunc<T, T, T>; InitValue: T): T; 
var 
  I: T; 
begin 
  Result := InitValue; 
  for I in InputArray dobegin 
    Result := ReduceFunction(Result, I); 
  end; 
End;

5. 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;

6. 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:

classfunction HigherOrder.Filter<T>(InputArray: TArray<T>; 
    FilterFunction: TFunc<T, boolean>): TArray<T>; 
var 
  I: Integer; 
  List: TList<T>; 
begin 
  List := TList<T>.Create; 
  tryfor I := 0 to length(InputArray) - 1 doif FilterFunction(InputArray[I]) then 
        List.Add(InputArray[I]); 
    Result := List.ToArray; 
  finally 
    List.Free; 
  end; 
end;

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.

There's More!

Higher-order functions are a vast and interesting topic, so in this recipe we only scratched the surface. One of the main concepts is the abstraction of the internal loop over the data structure. Consider this: by abstracting the concept of looping, you can implement looping in any way you want, including implementing it in a way that scales nicely with extra hardware.

A good sample of what can be done using functional programming is the parallel extension of the OmniThreadLibrary (a nice library that simplifies multithreading programming), written by Primož Gabrijelčič. This is a simple code sample that executes a parallel function for defining a single iteration with an anonymous method and runs it using multiple threads:

Parallel.ForEach(1, 100000).Execute( 
    procedure (Const elem: integer) 
begin 
      //check if the current element is  
      //a prime number (can be slow) 
   if IsPrime(elem) then  
        MyOutputList.Add(elem); 
    end);

You have just read an excerpt taken from the third edition of Delphi Cookbook written by Daniele Teti and Daniele Spinetti published by Packt.

The book can be used as a back to front guide to developing Delphi apps or a reference for a collection of best practices.

Packt - Delphi Cookbook - Third Edition

Delphi (programming language) Data (computing) Element Filter (software)

Opinions expressed by DZone contributors are their own.

Related

  • Required Capabilities in Self-Navigating Vehicle-Processing Architectures
  • Process Mining Key Elements
  • Reversing an Array: An Exploration of Array Manipulation
  • Low Code/No Code Testing Approach for Salesforce Testing

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook