Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Printing Envelopes, and Lambda Expressions

Making use of the new lambda expressions in Visual Studio 2008 can eliminate lengthy loops.


advertisement
or those following the saga of my household's two cross-country moves in nine months back in 2005, I'm sure you'll be happy to know that the current consensus is that we've settled in the woods in Northern California, and couldn't be happier. Almost 24 months later, life is good. As I write, I look out my window and see lots of flora and the occasional wild critter. I've managed to get involved with the local community theater, and I'm heading out to play piano for a rehearsal for this summer's gala production of "Bye Bye Birdie" as soon as I finish this article. We endeavor to bike two or three times a week over hilly and somewhat treacherous terrain (there was a major spill last summer-I'll spare you the scary photos). Life is, to be honest, good.

One of the best parts of this move is the juxtaposition of the terrible neighbors back in condo-land on the right coast (their constant screaming at each other through paper-thin walls was the final straw that sent us packing, and luckily, their interest in expanding into our condo made the sale quick and lucrative) with the totally delightful (albeit slightly more removed, geographically) couple next door. She's a real estate consultant a bit older than me, and he's a retired rocket scientist, about my father's age. The first week we were here, they wandered over to regale us with stories of the crazy couple that had built our house, to provide bread and cheese, and to offer his tools ("I have every tool ever invented", he said, and he does) in case we needed to build anything.

She's quite comfortable with the computer; he, on the other hand, is fiercely independent, but a little more unclear on the concept. Case in point: He hasn't quite figured out how to handle repetitive processes. For example, as his retirement project, he's managing the construction, from scratch, of a full-size steam locomotive at the local railroad museum. It's a big project, and requires some help with funding in the form of pleas for money to everyone he knows. (We've contributed. You can too, if you're into that sort of thing: Visit http://www.ncngrrmuseum.org/ and click the links dealing with the steam engine.) Quarterly, he has historically printed out color photos of the current status of the project, along with a cover letter, to mail to current and potential contributors. I caught him once printing the color photos on his little ink-jet printer (taking something like fifteen minutes per page). It hurt to watch, so I volunteered to print them here on the color laser—now the process takes two minutes or so for the stack of pages.

Last time this all transpired, I started to wonder how he was addressing the envelopes. I figured he had a spreadsheet or database, and was printing labels. Not so. Instead, he was typing each address by hand in Microsoft Word, and inserting the envelopes into the inkjet printer one at a time, each time he sent a mailing. After 100 envelopes, this might get old. Especially, the tenth time you've done it. It is, at best, a six-hour job. Unable to keep myself from doing such things, I suggested that next time, he create a spreadsheet, email it to me, and I'd print the labels in a minute or so using the cute little Dymo label printer sitting here on my desk. (Off topic: I've never had much luck with third-party add-ins for Office products that deal with printing, but Dymo has done a bang-up job. This add-in allows you to select a region of an Excel spreadsheet, for example, and it iterates through all the rows and prints neatly formatted address labels. Cool!) In any case, by automating this repetitive process, we've reduced my neighbor's efforts from more than a day to print everything out, to just a few minutes of my time. I really can't stand to see people waste time with these sorts of processes, when they can be automated. (I've also taken over the chore of general IT manager next door, as well. I've set up networks, reformatted hard drives, ordered laptops, and configured wireless settings. On the other hand, they care for our cats when we're out of town. It's a good tradeoff.)

The same compulsion applies to programming—I even hate having to write loops when I don't have to. If I want to take an action on each item in a collection, I'd much rather hand the collection a reference to a method to call for each member, and let it handle the looping part. Why should I have to insert each envelope manually (to mix metaphors)?

As I discovered when learning the .NET Framework 2.0 changes, the System.Array and System.Collections.Generics.List classes provide just this functionality, allowing you to specify a predicate instance that defines the action you'd like to apply to each member of the collection. You simply need to call a method of the collection that accepts a reference to a procedure that handles a single member, and the collection class does the rest. I was excited about this functionality, I wrote an article about it for MSDN Magazine—if you're new to the concept, you might want to read up on it here.

To quickly summarize a part of that article, imagine that you have generic List containing FileInfo instances, and you'd like to retrieve a List object containing all the files whose size is smaller than 500 bytes. Rather than looping through the List, and—for each file that matches the criterion—copying the item into a new List, you can List.FindAll method. To do this in Visual Studio 2005 you first need to create a method of the System.Predicate delegate type, perhaps like this:

[Visual Basic] Private Function IsSmall( _ ByVal file As FileInfo) As Boolean Return file.Length < 500 End Function [C#] private bool IsSmall(FileInfo file) { // Return True if the file's length // is less than 500 bytes. return file.Length < 500; }

Then, you can call the List.FindAll method, which returns a new List instance containing all the items that met the criterion you specified:

[Visual Basic] Dim subList As List(Of FileInfo) = _ fileList.FindAll(AddressOf IsSmall) [C#] List<FileInfo> subList = fileList.FindAll(IsSmall);

The problem with this technique (at least, for Visual Basic developers) is that you must create a separate procedure (IsSmall, in this case) of the specified delegate type (System.Predicate, in this case) in order to use the FindAll method, or any of its "friends" that work similarly. C# developers can use anonymous delegates to do the work without creating a separate procedure, like this:



[C#] List<FileInfo> subList = fileList.FindAll( delegate(FileInfo file) { return file.Length < 500; });

Visual Basic 2005 developers could only gaze wistfully at such code, partially satisfied in that they could call the FindAll method and its friends at all, even if it meant creating a separate procedure to do the actual work.

With the addition of lambda expressions in Visual Studio 2008, Visual Basic developers now can feel as compact as C# developers. This new feature, specifically added to support LINQ in Visual Basic, makes it possible to call methods like List.FindAll without creating a separate delegate instance callback function. The syntax may be a bit funky, but it works much like C#'s anonymous delegates. In Visual Basic 2008, in situations in which you might have used the AddressOf keyword, you can now use the Function keyword, specifying the actual function body inline. (The limitation here is that the syntax doesn't support the return keyword—the assumption is that the expression you supply after the Function keyword returns the required value—and there's no way to create a lamdba expression that doesn't return a value.)

Revisiting the previous example, you could reduce the code in Visual Basic 2008 by removing the IsSmall method, and rewriting the call to the FindAll method like this:

[Visual Basic] Dim subList As List(Of FileInfo) = _ fileList.FindAll( _ Function(ByVal file As FileInfo) _ file.Length < 500)

Remember, the point of the delegate instance is to accept a FileInfo object, and if its size is less than 500, return True. That's exactly what this "anonymous delegate" does—it defines an inline function that accepts a FileInfo object, and returns the value of the expression that makes up the body of the function/lambda expression.

Lambda expressions are, as I mentioned, somewhat limited, and although they're really intended to be used as part of queries in LINQ, they sure do help clean up simple operations like this. Will they handle every situation in which a C# developer might use an anonymous delegate? Not really. But they're a step in the right direction.

If you find yourself tackling repetitive tasks the hard way, think of my poor neighbor, typing address after address each quarter, sliding each envelope into the inkjet printer individually. If you've got the tools to speed up your work, use them. In this case, lambda expressions in Visual Basic 2008 solve some specific, but bothersome, programming challenges.

Editor's Note: This article was first published in the September/October 2007 issue of CoDe Magazine, and is reprinted here by permission.



   
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap