MoMonkey 的个人资料Monkey Coder日志列表网络 工具 帮助

日志


10月30日

The Great Debate

I found this interview/debate with Mads Torgersen (C# guy), Joe Armstrong (Erlang guy) and Erik Meijer (Haskell guy) very entertaining!
It's a classic debate. I remember hearing it in the 25-year-old SICP lectures in which Hal Abelson said,

“Generally, I think what you're seeing is that we're running across what I think's a very basic problem in Computer Science; which is how to define languages that somehow can talk about delayed evaluation but also be able to reflect this view that there are objects in the world. How do we somehow get both? I think that's a very hard problem and it may be that it's a very hard problem that has nothing to do with Computer Science. It really is a problem with having two very incompatible ways of looking at the world.”

But hey, before you start thinking that Erlang must be the answer to all the world’s problems Smile, you should read this post showing how to do the same message passing style w/async workflows and MailboxProcessor in F#: http://strangelights.com/blog/archive/2007/10/24/1601.aspx

10月24日

Map / Reduce / Filter

I just did a little Functional Programming brownbag talk today. One example that went over pretty well was this:

double CheapGasNearby(IEnumerable<GasResult> results)
{
   
double min = double.MaxValue;
   
foreach (GasResult r in results)  {
       
if (r.Distance < 5.0)  {
           
double price = r.Price;
           
if (r.Name == "Safeway")
                price *= 0.9;
           
if (price < min)
                min = price;
        }    }
   
return min;
}

This works to find the minimum price for gas within 5 miles; taking into account a 10% Safeway discount. Ahhh! Mutation and mixing of intent all over the place!
 

Let’s see if we can, most importantly, separate out the intentions and also get rid of the use of assignment:

double CheapGasNearby(IEnumerable<GasResult> results)
{
    double min = double.MaxValue;
    foreach (GasResult r in results)  {
        if (r.Distance < 5.0)  {
            double price = r.Price;
            if (r.Name == "Safeway")
                price *= 0.9;
            if (price < min)
                min = price;
        }    }
    return min;
}

Examining the code in red, you notice that it's essentially doing a map (just converting a collection of GapResults to adjusted doubles), and the green is a reduce operation (threading the min accumulator throughout the iteration) and the purple is obviously a filter. This can be rewritten in a much more straightforward way using the functional extension methods hanging off of IEnumerable in .NET 3.5+:

double CheapGasNearby(IEnumerable<GasResult> results)
{
    return results
       
.Where(r => r.Distance < 5.0)
       
.Select(r => r.Price * (r.Name == "Safeway" ? 0.9 : 1.0))
       
.Aggregate(double.MaxValue, (m, p) => p < m ? p : m));
}

Of course there’s already a Min() method available and additionally it’s even more succinct using LINQ keywords:

double CheapGasNearby(IEnumerable<GasResult> results)
{
    return (from r in results
         where r.Distance < 5.0
         select r.Price * (r.Name == "Safeway" ? 0.9 : 1.0)
        ).Min()
}

Why would we ever want to write it in the "mini-Rube Goldberg machine" imperative style?

 

Here's the brownbag deck if you're interested...