Building a Generic Statistics Library, Part 5: Implementation

Posted by on in Blogs
This post is the last in my series on building a statistics library using Delphi 2009’s new generic types.  If you’re new to the series, you might want to start with the first post. Having implemented the functions FoldL and Map, and having used FoldL to implement Count and Sum, implementing Average and StandardDeviation are now trivial:

class function TStatistics<T>.Average(
const AData: TEnumerable<T>;
AAdder: TBinaryOp<T>;
ADivider: TBinaryOp<T>;
AExplicitCast: TFunc<integer, T>): T;
Total, Num: T;
Total := Sum(AData, AAdder, AExplicitCast);
Num := Count(AData, AAdder, AExplicitCast);
Result := ADivider(Total, Num);

class function TStatistics<T>.StandardDeviation(
const AData: TEnumerable<T>;
ADeviation, AAdder, ADivider:TBinaryOp<T>;
ARoot, ASquare: TUnaryOp<T>;
AExplicitCast: TFunc<integer, T>): T;
Avg, Variance: T;
SquaredDeviations: TEnumerable<T>;
Avg := Average(AData, AAdder, ADivider, AExplicitCast);
SquaredDeviations := Map(
function(AValue: T): T
Result := ASquare(ADeviation(AValue, Avg));
Variance := Average(SquaredDeviations, AAdder, ADivider, AExplicitCast);
Result := ARoot(Variance);

You can download the complete project from CodeCentral.

Even having to use functions like "AAdder" in place of the more conventional "+", I think the algorithm is still fairly easy to read here, at least if you understand the standard deviation.  As Raymond points out in comments to the previous post, however, the syntax may not be familiar to see folks who have spent most of their time in older versions of Delphi.

I'm afraid you're just going to have to get used to it.  Delphi is well behind the pack in getting support for anonymous methods, and in many environments it is difficult to get much done without them.  JavaScript comes to mind; the asynchronous nature of much of what you have to do in JavaScript makes a continuation-passing style quite practical.  Lambda expressions, which are, very roughly, a more concise version of an anonymous function, are required to do any useful work in LINQ.  In languages like Lisp and Haskell, all "lines" of code are (again, very roughly) functions, which are anonymous by default. Few mainstream languages don't support this, and many environments leverage it heavily. If you don't understand such syntax well, you will find it hard to work outside of Delphi, like a COBOL programmer who doesn't understand OO. You aren't required to like it, but you have to understand it.

Tags: Delphi


  • Guest
    El Cy Wednesday, 24 September 2008

    Great series of articles covering generics and anonymous methods ... Hopefully we will see more coming into the future !

    The best example of mainstream language (#1 actually) that have no closures/anonymous methods is ... Java (it is alot debated if Java 7 will have it, in the form proposed by Neal Gafter). So in this regard, Delphi is with a step in front !

  • Guest
    Tony Wednesday, 24 September 2008

    Excellent article. Any way you can convert the project to C# ?


  • Guest
    Sergey Antonov Wednesday, 24 September 2008

    Craig Stuntz, good evening.

    The best way to overcome operators using prohibition in .NET on typed parameter is to use power of native code.
    The best solution is to allow native Delphi compiler to be an independent compiler without caution to Delphi.NET ones.
    I repeat again and again

    -No constraints is required to achieve safe code in native.
    -Constraints may be usefull only as syntax sugar (some form of hack(smart) code to guarantee safe code).

    So if we need AExplicitCast(but in yours article it is just a initializer) to T.
    We just declare cast operator in T type.
    if we need Binary operator on T and U,
    (in yours series of articles it is restricted to binaryOp(T,T))
    we just declare Binary operator in T or U.

    What do you think?

    Thanks, Sergey.

  • Guest
    Sergey Antonov Wednesday, 24 September 2008

    Craig, in native Delphi there is no IL, and therefore no code would be generated at declare time of generic type in opposition to .NET.
    The main difference is that generic IL code generated by compiler in .NET at compile time. Native code is generated at run time.

    On native delphi native code is generated at compile time.
    There is no way to instantiate generic class at run time in native delphi.
    So package can not expose generic class. Package can only expose the closed constructed type. So no problem to interoperate with closed constructed type. And no constrains are required.

    So there is no way to instantiate package generic class by external environment without having acess to generic code(i.e. source code) in native delphi.

  • Guest
    Sergey Antonov Wednesday, 24 September 2008

    > I haven’t tested, but I would presume that the compiler reuses compiled, >binary specializations for reference types, in other words, more like C# then >C++ templates.

    Craig, I have read
    "Design and Implementation of Generics for the.NET Common Language Runtime" by Andrew Kennedy Don Syme.

    But share "work native code" in .NET for reference typed parameters is only valid on .NET because copy semantic is equal for all reference types
    ("o" types in terms of .NET), i.e. no copy semantic special operations, just analizing by GC roots in evaluation stacks slots in GC time.

    But on native Delphi there are so many reference types with absolutely different copy semantic:

    1. class intances
    2. interfaces(_addref, _release of unknown)
    3. dynamic string and arrays.
    4. pointers

    What reference type do you have in mind?

    So there is no way to share "native code" for all reference types in native Delphi.

    C# and Delphi are strongly type languages.

    Constrains is solution to guarantee safe code at generics run time instantiation in .NET. It is just a way to guarantee compile time IL code generation to support types parameters supplying at run time.
    So no run time instantiation, no contraints needed.

  • Guest
    Raymond Wednesday, 24 September 2008

    Thanks for the excellent series of posts that show some of the power (and some of the restrictions!) of generics and anonymous methods when used together.

    As to the syntax, I'm sure I'll get used to it ;-) I still have misgivings over the whole TUnaryOp issue, though this is from performance rather than semantic or syntactic points of view.

    Ultimately it's another tool in the toolbox and should be used where appropriate. There's an old adage that applies here, I think: "To a man with a hammer, every problem looks like a nail"


  • Guest
    Sergey Antonov Wednesday, 24 September 2008

    Craig, good afternoon.

    So far as I known

    .NET 1.0 CIL already support some kind of generic behaviour.

    For instance the ldarg & starg IL instructions are generic since .NET 1.0.

    Since all value types have its associated reference type equivalents(boxed form) there is a generic way to call root of references type System.Object methods. But to do this safe you must to box typed parameter to quaranteee unified interface invoking for native code(via VMT) since all reference types have System.Object virtual methods and boxed value types
    (it is just wrapped value type by reference type) too.
    So no problem in quarantee safe code.
    Here constraints are not needed.

    But if need to quarentee run time safe call methodA on type parameter and compile time generated IL code, we have to do additional actions to quarantee safe and suitable compile time generated IL code for type parameters, i.e. we have to declare constraints.
    But if instantion occurs(native code generation) at compile time there is no needs to constraint at all, because compiler just analize the IL code to infer violations. JIT can do this at run time, but it is time consuming operation. But there is way to simplify this operation by generalization unsafe operations in constraints and to check constaraints.

    So people who say that .NET is a single rooted dissemble at little. All references type are single rooted. But not a value types in there value form.
    Value types have identical methods name as in Sytem.Object, but not are inherited from Sytem.Object, where it's boxed form are.

  • Please login first in order for you to submit comments
  • Page :
  • 1

Check out more tips and tricks in this development video: