The Swiss Army knife of Data structures ... in C #

Source: Internet
Author: User

"I worked up a full implementation as well" I decided it is too complicated to post in the blog. What I am really trying to get across is that immutable data structures were possible and not this hard; A full-on finger tree implementation is a bit too much for that purpose. " I am happy to correct my post and acknowledge that a previous C # implementation did exist. Hopefully, Eric would publish his implementation.
So, the work discussed are most probably the first published Finger Tree implementation in C #.

Background :
Created by  ralf Hinze  and  Ross Paterson  in 2004, and based to a large extent on the work of  Chris Okasaki  on&nbs P implicit Recursive slowdown and catenable double-ended Queus , this data structure, to quote the abstract of  the paper introducing Finger Trees , is:

"a functional representation of persistent sequences supporting access to the ends in amortized constant time< /c3>, and concatenation and splitting in time logarithmic in the size of the smaller piece. Representations achieving these bounds has appeared previously, but 2-3 finger trees is much simpler, as is the Operati ONS on them. Further, by defining the split operation in a general form, we obtain a general purpose data structure that can serve As a sequence, priority queue, search tree, priority search Queue andmore. "

Why the finger tree deserves to being called the Swiss knife of data structures can best being explained by again quoting the in Troduction of the paper:

"The operations one might expect from a sequence abstraction include adding and removing elements at both ends (the deque o perations), concatenation, insertion and deletion at arbitrary points, finding an element satisfying some criterion, and s Plitting the sequence into subsequences based in some property. Many efficient functional implementations of subsets of these operations is known, but supporting more operations Efficie Ntly is difficult. The best known general implementations is very complex, and little used.
This paper introduces functional 2-3 finger trees, a general implementation the performs well, but is much simpler than p Revious implementations with similar bounds. The data structure and its many variations is simple enough, we is able to give a concise yet complete executable de Scription using the Functional programming language Haskell (Peyton Jones, 2003). The paper should is accessible to anyone with a basic knowledge of Haskell and its widely used extension to Multiple-param Eter type classes (Peyton Jones et al., 1997). Although the structure makes essential use of laziness, it's also suitable for strict languages that provide a lazy evalu ation Primitive.
"

efficiency and universality are the both most attractive features of finger trees. Not less important are simplicity, as it allows easy understanding, straightforward implementation and uneventful Maintenance.

Stacks support efficient access to the first item of a sequence only, queues and deques support efficient access to both E NDS, but not to an randomly-accessed item. Arrays allow extremely efficient O (1) access to any of the their items, but is poor at inserting, removal, splitting and conc Atenation. Lists is Poor (O (N)) at locating a randomly indexed item.

Remarkably, the finger tree is efficient with all these operations. One can use this single data structure for all these types of operations as opposed to have to use several types of data Structures, each of the efficient with the only some operations.

Note also the words functional and persistent, which mean that the finger tree was an immutable data stru Cture.

In. NET the ilist<t> interface Specifies a number of void methods, which change the list in-place (so the I Nstance object is mutable). To implement an immutable operation one needs first to make a copy of the original structure (list<t>, Linkedlist<t>, ..., etc). An achievement of . NET 3.5 and LINQ are that the set of new extension methods (of theEnumerable Class) Implement immutable operations.

In the year, Finger Tree implementations has been known only in a few programming languages:in Haskell, in OCaml, and in Scala. At least the popular search engines say.

What's about a C # implementation? In February Eric Lippert had a poston his blog about finger trees. The C # code he provided does not implement all operations of a Finger Tree and probably this is the reason so this post I s referred to by the Wikipedia only as "Example of 2-3 trees in C #", but not as an implementation of the Finger T REE data structure. Actually, he did had a complete implementation at that time (see the Update at the start of this post), but desided not t o Publish it.

My Modest contribution is what I believe to being the first published complete C # implementation of the Fin GER Tree data structure as originally defined in the paper by Hinze and Paterson (only a few exercises has not been imple mented).

Programming a Finger Tree in C # is as much fun as challenge. The finger tree structure is defined in an extremely generic. At first I even is concerned that C # might not being sufficiently expressive to implement such rich genericity. It turned out of that C # lived up to the challenge perfectly. Here are a small example of how the code uses multiple types and nested type constraints:

Types:

U-the type of Containers that can be split

T-the type of elements in a container of type U

V-the type of the measure-value when a element is measured

Public class Split<u, T, v>
where U: isplittable<t, v>
where T: imeasured<V>

{
..........................................................
}

Another challenge was to implement lazy evaluation (the. NET term for this are "deferred execution") for some of the method S. Again, C # is up to the challenge with its IEnumerable interface and the ease and finesse of using the "yield return" s Tatement.

The net result:it is possible to write code like this:

Public Override IEnumerable<T> tosequence ()
{
viewl<t, m> lview = Leftview ();
yield return Lview.head;

foreach (T T in LView.ftTail.ToSequence ())
yield return t;
}

Another challenge, of course, was, the one definitely needs to understand Hinze ' s and Ross ' article before even trying to Start the design of an implementation. While the text should is straightforward to anyone with some Haskell and functional programming experience, it requires a  Bit of concentration and some very basic understanding of fundamental algebraic concepts. In the text of the article one would find a precise and simple definition of a monoid. My first thought was, such academic knowledge would not really is necessary for a Real-world programming task. Little did I know ... It turned out that the monoid plays a central role in the generic specification of the objects that has a Measure.

I was thrilled to code my own version of a monoid in C #:

Public class monoid<T>

{

T Thezero;

 
Public Delegate t monop(t t1, T T2);

Public Monop theop;

Public monoid (T tzero, monop Amonop)

{

Thezero = Tzero;

Theop = Amonop;

}

Public T Zero

{

Get

{

return Thezero;

}

}

}

Without going into Too-much details, here's how the correct monoids was defined in suitable auxiliary classes to be used In defining a random-access Sequence, priority Queue and Ordered Sequence:

Public static class Size

{

public static monoid<uint> themonoid =

New monoid<uint> (0, new monoid<uint&G t;. Monop(ANADDOP));

public static uint ANADDOP (uint S1, uint S2)

{

return s1 + s2;

}

}

public static Class Prio

{

public static monoid<double> themonoid =

New monoid<double>

(Double. NegativeInfinity,

New Monoid<double>.monop (AMAXOP)

);

public static double Amaxop (double D1, double D2)

{

Return (D1 > D2)? D1:D2;

}

}

public class key<t, v> where v:icomparable

{

Public delegate V GetKey (T t);

Maybe we shouldn ' t care for Nokey, as this is too theoretic

Public V Nokey;

Public GetKey keyassign;

Public Key (V Nokey, GetKey keyassign)

{

This. Keyassign = keyassign;

}

}

public class keymonoid<t, v> where v:icomparable

{

Public key<t, v> keyobj;

Public monoid<v> themonoid;

Public v Anextkeyop (v v1, v v2)

{

Return (V2.compareto (keyobj.nokey) = = 0)? V1:v2;

}

Constructor

Public Keymonoid (key<t, v> keyobj)

{

This. Keyobj = Keyobj;

This.themonoid =

New Monoid<v> (Keyobj.nokey,

New Monoid<v>.monop (ANEXTKEYOP)

);

}

}

Yet another challenge is to being able to create methods dynamically, as currying is essentially used in the Speci Fication of finger trees with measures. Once again it is great to make use of the existing. NET 3.5 infrastructure. Below is my simple FP static class, which essentially uses the. NET 3.5 Func object and a lambda Expression in order to implement currying:

Public static class FP

{

Public Static Func<y, z> curry<x, Y, z>
(this func<x, Y, z> Func, x x)

{

return (Y) = func (x, y);

}

}

And here is a typical usage of the currying implemented above:

Public T Elemat (UINT IND)

{
return treerep.split

(new mpredicate<uint>

(

FP. curry<UINT , uint, bool> (THELESSTHANIMETHOD2, Ind)

),

0

). Splititem.element;

}

Now, a for all who has reached this point of my post, here are the link to the complete implementation.

Be reminded once again the. NET 3.5 is needed for a successful build.

In my next posts I'll analyze the performance of this Finger Tree implementation and what it fares compared to existing I Mplementations of sequential data structures as provided by different programming languages and environments.

The Swiss Army knife of Data structures ... in C #

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.