Click here to Skip to main content
Click here to Skip to main content

Using Nested Lambda Functions to Implement Numerical Algorithms

, 19 Jul 2010
Rate this:
Please Sign up or sign in to vote.
For numerical computations, coding with lambda functions can substitute for the want of nested methods in C#.

Introduction

Some twenty-seven years ago I had occasion to write some numerical methods in Pascal. Many algorithms used nested functions. This article describes how I successfully migrated many of these functions to C# 4.0 using nested lambda functions.

I was designing a yacht and the problem was to produce not only a fair surface but to calculate displacement, centre of buoyancy and wetted surface area, amongst other data arising from the hull surface definition. At the time there were no CAD programs available that could run on my BBC computer. It boasted an 8 bit 6502 processor, running at 2 MHz and 32 kbytes of RAM. In practice, at that time, hull lines were plotted full scale and smoothing was accomplished with a flexible wooden batten or spline. Measurements were taken from lofted lines or scale drawings and Simpson’s rule was used, with hand calculations, to determine displacement, centre of buoyancy and other data arising from the hull shape.

At that time, there was a Pascal compiler for the BBC computer. I was also able to program a function that specified the hull. Then, by using numerical computations, I could generate the required hull data. Moreover, I was able to iteratively modify the parameters that specified the hull and besides other desirable capabilities, optimize wetted surface area with a required heeling moment. Admittedly the graphics were absolutely rudimentary but by using general conics as fairing curves, a fair hull could be obtained.

The 12 m yacht, Apollonius (note those conics), a cruiser racer, was built. This yacht has successfully completed many blue water ocean races. These days I find more pleasure in cruising about the islands of Bass Strait, a turbulent stretch of water between Tasmania and the Australian mainland.

Background

Let’s leave this digression. Last week, when clearing out some old cardboard folders, I came across some of my old Pascal code, just readable, produced on a long extinct dot matrix printer. It went something like this:

program boatHull(input, output);
type posint = 1..maxint;
{Much code omitted - for brevity}

    function integral(function f(x : real) : real; a, b : real; n : posint) : real;
    type
    {types} ;
    var
    {vars};
    begin
        {chosen algorithm}
    end {integral};

   function doubleIntegral (function f(x, y : real) : real;
                   function g(y : real): real; function h(y : real): real; a,
                    b : real; m, n: posint ) : real;
                    {end of parameters}
       function area(y: real): real; {
            first nested function; note g, h, and m are in scope}

            function argument(x : real) : real; {
                second nested function; note y is in scope}
            begin {argument}
                argument := f(x,y)
            end {argument};

        begin {area}
            area := integral(argument, g(y), h(y), m)
        end {area};

    begin {doubleIntegral}
        doubleIntegral:= integral(area, a, b, n)
    end {doubleIntegral};

begin {demo}
   {input, output code goes here}
end. {boatHull}

We can see that the scopes of the functions integral and doubleIntegral make them available to the program boatHull. However, the function doubleIntegral is able to call the function integral. The function area is nested within doubleIntegral and is only available to doubleIntegral. Similarly the function argument is only available to the function area.

Encapsulation is the compelling reason for using nested functions in the Pascal function doubleIntegral. The nested function area has no significance outside doubleIntegral and neither does the function argument have any reason to exist outside the area function.

Moving to C# 4.0

In my retirement I design and implement online SQL database applications for educational environments. I use C# for code-behind in .aspx pages. Naturally I wondered how, using coding principles similar to the above, a double integral could be achieved using nested functions.

In C#, Pascal functions correspond to methods. However, a direct translation of the code is not possible as C#, although supporting nested classes, does not support nested methods.However, lambda functions, relatively new to C#, may be nested. So, I downloaded Visual C# 2010 Express, a consumer of .NET 4.0, to have a go.

At this stage, if readers are unfamiliar with lambda notation and delegates, then they should consult an appropriate reference, of which there are many.

First, after preliminaries, some delegates:

using System;
using System.Linq;
 
namespace Computationals
{
    // Delegates
 
    delegate double Function(double x);
    delegate double Functionxy(double x, double y);
    delegate double Integral(Function f, double a, double b, int n);
    // for integration of function f over n subintervals from a to b
 
    public class LambdaNumericals
    {

Next, some integration methods: Firstly, Simpson’s Method

static Integral SimpInt()
{
// Simpson's rule 
// integration of function f, over n (even) subintervals in the interval [a,b]
Integral integral = (f, a, b, n) =>                
  {
// n, not even, throw exception code should go here
  double sum = 0;
  double h = (b - a) / n;
  for (int i = 0; i < n; i = i + 2)
     sum = sum + (f(a + i * h) + 4 * f(a + (i + 1) * h) + f(a + (i + 2) * h)) * h / 3;
  return sum;
  };
return integral;
}

Secondly, the Gaussian-Legendre 4 point Algorithm

 static Integral GaussInt()
{           
// 4-point Gaussian rule integration of the function f, 
// over n subintervals in the interval (a,b)
 Integral integral = (f, a, b, n) =>
    {  
     double[] weight = new double[4];
     weight[2] = 0.652145154862546;
     weight[1] = weight[2];
     weight[3] = 0.347854845137454;
     weight[0] = weight[3];
 
     double[] point = new double[4];
     point[3] = 0.861136311594053;
     point[0] = -point[3];
     point[2] = 0.339981043584856;
     point[1] = -point[2];
 
     double sum = 0;
     double h = (b - a) / n;
 
     double xr, xl, m, c, s;
 
     for (int i = 0; i < n; i++)
        {
         s = 0;
 
         xl = a + i * h;
         xr = xl + h;
         m = (xr - xl) / 2;
         c = (xr + xl) / 2;
 
         for (int j = 0; j < 4; j++)
           s = s + weight[j] * f(m * point[j] + c) * m;
 
         sum = sum + s;
        }
     return sum;
    };
 return integral;
}

Now, we can use the above in:

 // Double integration using nested lambda functions
 // Inner integral dx, from x=g(y) to x=h(y), over m subintervals, using algorithm inInt
 // Outer integral dy, from y=a to y=b, over n subintervals, using algorithm outInt
 
static double dubInt(Functionxy fxy, Function g, Function h, double a, double b, 
        int m, int n, Integral inInt, Integral outInt)
   {
      return outInt((double y) => inInt((double x) => fxy(x, y), g(y), h(y), m), a, b, n);
  }

inInt and outInt are parameters accepting integration functions, SimpInt() or GaussInt().
The Pascal doubleIntegral function becomes outInt(area, a ,b, n).
The area function in the Pascal code corresponds to the snippet (double y) => inInt(argument, g(y), h(y), m).
Finally the argument function corresponds to (double x) => fxy(x, y).

To test our code we can define the following test functions to be used as parameters:

public static double p(double x, double y)
        {
            return x * y;
        }
 
        public static double f1(double x)
        {
            return x;
        }
 
        public static double f2(double x)
        {
            return 2 * x;
        }
 
        public static double sine(double x)
        {
            return Math.Sin(x);
        }

We can then invoke our integration methods using:

public static void Main()
  {
   Integral integrate1, integrate2;
 
   integrate1 = SimpInt();
   Console.WriteLine(integrate1(sine, 0, Math.PI, 60));
   // result 2.00000008353986
 
   integrate2 = GaussInt();
   Console.WriteLine(integrate2(sine, 0, Math.PI, 15));
   // result 2
 
   Console.WriteLine(integrate2(Math.Sin, 0, Math.PI, 15));
   // result 2
 
   // analytically, exactly 2 for the above integrals – 
   // note the superior convergence of Gauss 4-point algorithm
 
   // Now to test our double integration functtion
   Console.WriteLine(dubInt(p, f1, f2, 1, 2, 4, 4, integrate1, integrate2));
   // result 5.625
   // analytically 5 5/8
  }
}

Point of Interest

Following on, using similar strategies, I have applied lambda methodology to differentiation, double differentiation, curve length, partial derivatives, surface area and curvature. Code with examples is included is in the downloads.

The techniques demonstrated can easily be extended to integration in polar coordinates, triple integrals, and integration in cylindrical and spherical coordinates. Parametric functions, splines and surfaces and differential equations are possible candidates for lambda functions. No doubt this is only the tip of the iceberg.

Limitations on the Application of Code

In conclusion, I must emphasise that this article concerns the techniques of lambda and nested lambda functions applied to numerical computations. The actual numerical algorithms, in a library context would, of course, contain error specifications. Also, the best values for some constants are machine dependant. I make no suggestions on the suitability of the above for particular applications.

Nevertheless, once a programmer is comfortable with the syntax of lambda functions, they potentially can provide a remarkably succinct and productive entry into non trivial, numerical computations.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Julian Robinson
Other
Australia Australia
I am a retired Physics and Mathematics Teacher. For stimulation I now design SQL databases for online educational administration settings.
Most times I am relaxing at home but I have regular forays, lasting a couple of weeks, into Bass Strait on my yacht. I designed and built my timber composite 12m yacht after writing a CAD program, when none existed, in 1983.

Comments and Discussions

 
GeneralMy vote of 5 Pinmembermanoj kumar choubey27-Mar-12 4:15 
GeneralNice one Pinmemberbilo8128-Nov-10 4:45 
GeneralMy vote of 5 Pinmembermb1820-Jul-10 10:18 
GeneralMy vote of 5 PinmemberHoyaSaxa9319-Jul-10 11:30 
GeneralMy vote of 5 Pinmemberdefconhaya19-Jul-10 11:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140821.2 | Last Updated 19 Jul 2010
Article Copyright 2010 by Julian Robinson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid