Saturday, April 28, 2012

Closures come in many shapes!

The C# example below comes from a refactoring of the controller here. This is based on how Jon Skeet defines a closure on page 151 of his first edition of "C# in Depth." He makes a distinction between outer variables such as firstInteger and secondInteger which exist in the same method as the closure and are merely used by it, and a captured variable such as captured which gets effected by the closure. The x name for the ThreadStart comes from Mr. Skeet, so don't blame me for the odd variable name. :P It is important to note that if one does not run the line reading x(); that the magic will never happen and captured's shape will remain unchanged. This approach allowed me to drop that silly StatusQuoSustainer method from the controller as I no longer need a default for my case statement! Yay! As you can see, when x(); is run it considers captured and then effects the captured variable based upon its contents with the outer variables.

using System;
using System.Threading;
namespace DelegateExample.Controllers
{
   public class CalculationController : BaseController
   {
      public RenderJsonResult Go(string act, string one, string two)
      {
         string captured = act;
         Int32 firstInteger = Convert.ToInt32(one);
         Int32 secondInteger = Convert.ToInt32(two);
         ThreadStart x = delegate
                        {
                           switch (captured)
                           {
                              case "add":
                                 captured = Adder(firstInteger, secondInteger);
                                 break;
                              case "multiply":
                                 captured = Multiplier(firstInteger, secondInteger);
                                 break;
                              case "power":
                                 captured = Raiser(firstInteger, secondInteger);
                                 break;
                           }
                        };
         x();
         var result = new { calulation = captured };
         return RenderJson(result);
      }
      
      private string Adder(Int32 firstInteger, Int32 secondInteger)
      {
         return (firstInteger + secondInteger).ToString();
      }
      
      private string Multiplier(Int32 firstInteger, Int32 secondInteger)
      {
         return (firstInteger * secondInteger).ToString();
      }
      
      private string Raiser(Int32 firstInteger, Int32 secondInteger)
      {
         Int32 growingInteger = firstInteger;
         Int32 counterIntger = 1;
         while (counterIntger < secondInteger)
         {
            growingInteger = growingInteger * firstInteger;
            counterIntger++;
         }
         return growingInteger.ToString();
      }
   }
}

 
 

The next example is also of C#, illuminates the = () => operator, and comes from "C# 4.0 in a Nutshell: The Definitive Reference" by Joseph Albahari and Ben Albahari. The operator signifies a closure per page 132. I have modified the controller here to make this code. These closures allow us to pull the inbound values for a Func out of the Func's signature and instead set them on the other side of the operator. So instead of asserting that a Func<Int32,Int32,string> is equal to a method we may instead assert that a Func<string> is equal to a method while clarifying that the method takes two Int32 types for inbound parameters. Not only is this a little bit easier to read, but we are now no longer in a straightjacket in terms of restraints for inbound parameters. We may use a method of any manner of signature so long as the method returns the string. See how I was able to clean up StatusQuoSustainer. I removed an arbitrary variable once I was able to do so.

using System;
namespace DelegateExample.Controllers
{
   public class CalculationController : BaseController
   {
      public RenderJsonResult Go(string act, string one, string two)
      {
         Int32 firstInteger = Convert.ToInt32(one);
         Int32 secondInteger = Convert.ToInt32(two);
         Func<string> mathDelegate;
         switch(act)
         {
            case "add":
               mathDelegate = () => Adder(firstInteger, secondInteger);
               break;
            case "multiply":
               mathDelegate = () => Multiplier(firstInteger, secondInteger);
               break;
            case "power":
               mathDelegate = () => Raiser(firstInteger, secondInteger);
               break;
            default:
               mathDelegate = () => StatusQuoSustainer(firstInteger);
               break;
         }
         string thirdValue = mathDelegate();
         var result = new { calulation = thirdValue };
         return RenderJson(result);
      }
      
      private string Adder(Int32 firstInteger, Int32 secondInteger)
      {
         return (firstInteger + secondInteger).ToString();
      }
      
      private string Multiplier(Int32 firstInteger, Int32 secondInteger)
      {
         return (firstInteger * secondInteger).ToString();
      }
      
      private string Raiser(Int32 firstInteger, Int32 secondInteger)
      {
         Int32 growingInteger = firstInteger;
         Int32 counterIntger = 1;
         while (counterIntger < secondInteger)
         {
            growingInteger = growingInteger * firstInteger;
            counterIntger++;
         }
         return growingInteger.ToString();
      }
      
      private string StatusQuoSustainer(Int32 loneInteger)
      {
         return loneInteger.ToString();
      }
   }
}

 
 

The final example comes from Derek Greer and is of jQuery. What follows is a revision of the TryToCalculate() method here. I broke the sanity checking up into two hunks, one inside of another, to illustrate that one may create a closure and prep it for use before actually using it downstream in code. In crafting this last example I realized that I've misspelled growingInteger as growingIntger in all of my previous examples. Whatever.

function TryToCalculate() {
   var checky = "";
   var checkact = "" + $('input[name=act]:checked').val();
   checky = checky + checkact;
   if (checky.indexOf("undefined") < 0) {
      var calculationRules = function (act) {
         return function (firstInteger, secondInteger) {
            if (act == "add") return firstInteger + secondInteger;
            if (act == "multiply") return firstInteger * secondInteger;
            var growingInteger = firstInteger;
            var counterInteger = 1;
            while (counterInteger < secondInteger)
            {
               growingInteger = growingInteger * firstInteger;
               counterInteger++;
            }
            return growingInteger;
         };
      };
      closure = calculationRules(checky);
      var checkone = "" + $('input[name=one]:checked').val();
      var checktwo = "" + $('input[name=two]:checked').val();
      checky = checky + checkone + checktwo;
      if (checky.indexOf("undefined") < 0) {
         var castingOne = parseInt(checkone);
         var castingTwo = parseInt(checktwo);
         document.getElementById('equals').innerHTML = closure(castingOne,castingTwo);
      }
   }
}

No comments:

Post a Comment