• entries
122
246
• views
89932

# C# Functional Programming

586 views

There is a screenie of the app at work

its proving fairly useful to me thus far. If I am further motivated in this direction I may add functionality to more easily track comments in other peoples journals since checking if the journal author has responded to your comment can get a bit bothersome if you have posted in multiple journals. Meh, maybe the new site will fix this better ... someday...

I spent a couple hours doing minor tweaks on Mike's Line Counting app last night. His app filled a gap I needed between useless and overpriced and too antiuseless to be useful. One thing I am happy about is I got it to output csv so I can have graphs like:

figure 2.

Yay :s.

Anyways this post is mostly to highlight the fact that functional programming in C# 3.0 is not so bad. I implemented a feature, which counts lines of source code in folder(s) instead of projects. I was able to do this hassle free both cause Mike had had already done all the hard work and cause I used functional programming paradigms. In functional programming the ultimate goal is in being able to come up with a *useful* mathematics for specifications (based on category theory) and being able to easily translate this back and forth to algorithims and final language. The less the distance between these concepts the more elegant logical and behaviourally correct code you can write. The programming paradigm which is so far closest to this ideal is the one called 'functional'. Where the word functional is used to emphasize that statements and assignments are replaced with functions and composition and that functions can be be passed around and bound to variables.

I like functional programming not because of some highbrow nonsense like lack 'o state but because humans are much worse at tending to details than automatrons. Every less line of code, every bit of irrelevant implementation detail I can leave off my hands and relegate to the computer is the probability of an error and mistake reduced and my productivity and efficiency increased. Also perhaps due to my training in maths I may be more inclined towards the paradigm since I just may be afflicted by 'Deformation professionnelle'.

The basic/core concepts of functional programming are fold, map and filter. In basic terms map is a function which applies a function of type (a -> b) to a Collection (often a list but may be a tree or other graph) of type a and returns a collection of type b. What good is this? In C# Select stands in for map.

             var sourceFilesbyExtensions = new List();            //list of allowed extentions (e.g. cs, cpp, etc)            fileTypes.ForEach (ext => sourceFilesbyExtensions.Add(System.IO.Directory.GetFiles(folder, "*"+ext, searchOp )));                        var statsOfNodes = sourceFilesbyExtensions.Select(                               sourceFilesArray =>  sourceFilesArray.Select(sourceFile =>                                                             {  var fn = new FileNode();                                                               fn.Name = Path.GetFileName(sourceFile);                                                               fn.Path = sourceFile; fn.ChildNodes.Add(fn);                                                               CountFile(fn);                                                               return Count(fn, (new StringBuilder()), (new StringBuilder()), (new FileNode()));                                                                                                                           }).ToList()).ToList ();

With select I take a List and a function from string [] to List and get a List>. Fold is a an aggregating function. It takes a list/Collection of say type TypeA, an initial value for an accumulator of type TypeB and a function f(TypeB x, TypeA y) -> TypeB and so returns a value of type TypeB. It uses the function you passed to combine the accumulated value and an element from the collection till every element in the collection has been gone through and one arrives at its aggregate via your function. It expands out like this: f(..., f(x' ', f(x', f(x,y))) or f(x' ' ', f(x' ', f(x', f(...))).

A typical example of fold is how to sum a list of integers. C#'s fold is the Aggregate Function. IntList.Aggregate(0, (x,y)=>x + y); I use aggregate to Create a summary string from the individual summary strings held in each FileNode. I also use the Aggregate function to flatten a list of lists into one list. Google's mapreduce refers to the concept of Map and Flatten.

var undistinguishedNodeStats = statsOfNodes.Aggregate((p, q) => p.Concat(q).ToList());            Summary = undistinguishedNodeStats.Aggregate("", (s, n) => n.summary + s);

I will not go over filter because I have no example since I made no use of it here but it does what it sounds like it does. The whole source:

public void CalculateFromFolder(string folder, bool allDirectories)        {            var searchOp = (allDirectories) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;            var sourceFilesbyExtensions = new List();                       fileTypes.ForEach (ext => sourceFilesbyExtensions.Add(System.IO.Directory.GetFiles(folder, "*"+ext, searchOp )));            var statsOfNodes = sourceFilesbyExtensions.Select(                               sourceFilesArray =>  sourceFilesArray.Select(sourceFile =>                                                             {  var fn = new FileNode();                                                               fn.Name = Path.GetFileName(sourceFile);                                                               fn.Path = sourceFile; fn.ChildNodes.Add(fn);                                                               CountFile(fn);                                                               return Count(fn, (new StringBuilder()), (new StringBuilder()), (new FileNode()));                                                                                                                           }).ToList()).ToList ();            var ssb = new StringBuilder();                           var undistinguishedNodeStats = statsOfNodes.Aggregate((p, q) => p.Concat(q).ToList());            CodeLines = undistinguishedNodeStats.Sum(n => n.CodeLines);            Comments = undistinguishedNodeStats.Sum(n => n.Comments );            BlankLines = undistinguishedNodeStats.Sum(n => n.BlankLines);            TotalLines = undistinguishedNodeStats.Sum(n => n.TotalLines);            Summary = undistinguishedNodeStats.Aggregate("", (s, n) => n.summary + s);            CsvOutput = undistinguishedNodeStats.Aggregate("", (s, n) => n.csvOut  + s);            statsOfNodes.ForEach(fileNodes => {                                                 if (fileNodes.Count > 0) {                                                     var codeLines = fileNodes.Sum(n => n.CodeLines);                                                     var totalLine = fileNodes.Sum(n => n.TotalLines);                                                     ssb.AppendFormat("{0} files, Count {1}, Total Lines {2}, Code Lines {3} [Percentage: {4} %]",                                                         Path.GetExtension(fileNodes[0].Path), fileNodes.Count,                                                        totalLine, codeLines, Math.Round((double)codeLines/CodeLines * 100),1); ssb.AppendLine ("\r\n");}                                    });                                  Summary += ssb.ToString();                  }

I'm definitely going to be folding those changes back into my own version, although I always feel the need to take things too far, so I'll probably end up making use of Excel automation to produce the graphs for me or something [grin]

Also, maybe you could take your project in the next level and work for any arbitrary thread on GameDev.

Quote:
 Original post by Mike.Popoloski I'm definitely going to be folding those changes back into my own version, although I always feel the need to take things too far, so I'll probably end up making use of Excel automation to produce the graphs for me or something [grin] Also, maybe you could take your project in the next level and work for any arbitrary thread on GameDev.

Hehe Cool. As for that extension, its the same problem of priorities and motivation. I dont even feel entirely motivated to generalize the app to multiple journals. Plus with Forum threads you either (a) gotta count the # of replies yourself and keep track of # of pages Or (b) Look for the Topic Name across the pages of Active Topics and track reply count there.

However I have been thinking for a while about some kind of fun analysis that could be done on threads and method (a) would be the perfect platform to expand on for that. But I havent thought of any statistics or analysis on threads that interests me enough to jump priorities and propel me into action.

tst

Zee goggles, they do nothing!

If you're going to use linq, use linq!
A lot of that could be coalesced into a linq-query to be less repetitive. (eg)
        //
// Make the extension list a param if you like...
//
public static List<string> TargetFiles(string Folder, SearchOption opt) {
return(
new List<string>(
from fn in Directory.GetFiles(Folder,"*",opt)
join ext in new[] {"cs","cpp"} on 1 equals 1
where fn.EndsWith(ext)
select fn
)
);

}



Quote:
 Original post by Telastyn Zee goggles, they do nothing! If you're going to use linq, use linq! A lot of that could be coalesced into a linq-query to be less repetitive. (eg) *** Source Snippet Removed ***

Hmm. I find linq query syntax gets in the way sometimes. I needed the concept of a map from (string array list ) -> (FileNode list list) not to (string list) . Packing it all into one massive linq query obfuscates the code and does not help much for clarity or repetition. it would also hinder flexibility of code I feel. Its true though that I just recently decided to take a look at linq to see if it holds up to its functional programming roots. Ive been pleasantly surprised.

You were right though about redundancy, I was able to remove


listOfFileNodesList.ForEach(fileNodes => fileNodes.ForEach(file => CountFile(file)));

var statsOfNodes = listOfFileNodesList.Select(v =>
v.Select(c => { var z = Count (c, (new StringBuilder ()), (new StringBuilder ()), (new FileNode() )) ;
return z; }).ToList ()).ToList ();



by moving the CountFile and Count Functions to the original query. I guess I didnt notice that earlier as I implemented the algorithm without stopping to look at the bigger pic. Thanks. However, I purposefully maintain two lists, one flattened and another unflattened so I can collect stats per file type.

I also prefer the extension method syntax in most cases, and I know others do the same. It's mostly just personal preference; the same things can be done using both methods, it just depends on how you want it to look.

I've been a little out of the loop lately but that forum posting checker looks pretty ace.

Been meaning to write a moderator-specific one like that for a while, so you can get updates whenever your forum gets posted to. Along with some thresholds maybe to flag fast-moving topics (e.g. where a flamewar might be kicking off) or people bumping threads / reposting etc...

Keep up the good work and 'happy new year' [smile]
Jack