Share via


.NET: Using Lambdas to Write Clean Code

Introduction

When designing a method on an object we sometimes need another helper method to repeatedly perform some simple process, or encapsulate some long process, within the method we are trying to craft. The typical solution is to add another method to the object so that it can be called as needed. But when the code within these helper methods is rather exclusive to the method which will call it, you may not want the helper method exposed to other members of the same object.

This article will explore such a scenario and explain why and how lambda expressions can be used to address the issue.

Lambda Expressions

A lambda expression is simply a subroutine or function defined with a variable inside of another subroutine or function (collectively known as methods). Since a lambda variable is essentially a delegate pointing to a method, it can be called directly or used anywhere a delegate is expected. This allows us to define a method within another method.

And Everywhere that Mary Went

Suppose we wanted to design a method whose purpose was to shuffle a collection of items. By "shuffle" we mean: to reproduce the effect of performing any split-and-interleave style shuffle (e.g. riffle or weaving) on a deck of cards. This involves splitting the deck into roughly half and then taking a few cards at a time from each half and recombining them into a single collection. To get a good shuffle, we then need to repeat this process a certain number of times based on the size of the collection.

First we need a way to get a "rough number". This should be a method which takes a given value and returns a number which is plus-or-minus some small fraction of that number. We could implement this as a helper function named GetRoughNumber alongside the actual Shuffle() method that we are writing. The same is true for the SplitAndInterleave() method which implements a single pass of the shuffling algorithm. However, these helper methods (particularly SplitAndInterleave) contain functionality which is tuned specifically for use by the Shuffle() method, so wouldn't it be nice if we could make those helper methods internal to the Shuffle() method? That is precisely what lambda expressions allow us to do.

And everywhere the method went, the lambda was sure to go.

Code Example

The following code example demonstrates the scenario described above. A Shuffle() method defines a lambda method called roughNumber which takes a given value and returns a value which is plus or minus 7%. It also defines a lambda method called splitAndInterleave which implements a single pass of the split-and-interleave shuffle algorithm against a collection. Finally, the actual work of the Shuffle() method is nothing more than determining an optimal number of shuffles based on the size of the collection and then calling the splitAndInterleave lambda that many times in a loop.



      Public Function  Shuffle(Of T)(collection As IEnumerable(Of T)) As IEnumerable(Of T)
                 Dim random As New  Random  
              
                 'define a lambda expression used to get a rough number      
                 Dim roughNumber = Function(value As  Integer)  
                 Dim delta = CInt(Math.Ceiling(value * 0.07))  
                 Return value + random.Next(-delta, delta)  
      End Function
              
                 'define a lambda expression which implements a split-and-interleave algorithm      
                 Dim splitAndInterleave = Function(c As  IEnumerable(Of T)) As IEnumerable(Of T)  
                 Dim count As Integer  = c.Count  
                 'make use of the roughNumber lambda to get roughly half the collection      
                 Dim roughHalf As Integer  = roughNumber(count \ 2)  
                 Dim firstHalf As New  Stack(Of T)(c.Take(roughHalf))  
                 Dim secondHalf As New  Stack(Of T)(c.Skip(roughHalf))  
                 Dim result As New  List(Of T)  
                 Do      
                  Dim firstHalfCount = firstHalf.Count  
                  Dim allDone As Integer  = 0  
                  If firstHalfCount > 0 Then  
                
                  'again, use the roughNumber lambda to get a small random number of items      
                  Dim aFew = Math.Abs(firstHalfCount - roughNumber(firstHalfCount))  
                  While aFew > 0  
                   result.Add(firstHalf.Pop)      
                   aFew -= 1      
                   End While  
                  Else      
                  allDone += 1      
                  End If  
               
      Dim secondHalfCount = secondHalf.Count
                 If secondHalfCount > 0 Then  
                  'again, use the roughNumber lambda to get a small random number of items      
                  Dim aFew = Math.Abs(secondHalfCount - roughNumber(secondHalfCount))  
                  While aFew > 0  
                   result.Add(secondHalf.Pop)      
                   aFew -= 1      
                  End While  
                 Else      
                  allDone += 1      
                 End If  
              
                 If allDone = 2 Then Exit  Do  
               
      Loop  
                  result.Reverse()      
                  Return result.AsEnumerable  
      End Function
              
                 'determine the optimal number of shuffles based on the size of the collection      
                 Dim optimalShuffle = CInt(Math.Floor(((3 / 2) * Math.Log(collection.Count, 2) - 0.84)))  
                 'call the split-and-interleave lambda against the collection the optimal number of times      
                 For i As Integer  = 1 To  optimalShuffle  
                  collection = splitAndInterleave(collection)      
                  Next      
                 Return collection  
      End Function

Summary

In this brief article we have seen how lambda expressions can be used to encapsulate functionality within a method and how that, in turn, can allow us to achieve a cleaner class design. In the example shown, the class which implements the Shuffle() method does not need to be polluted by the helper methods needed by the Shuffle() method. This same concept can also be useful when implementing a method which internally requires recursive procedures.