12

Why might the C# language designers not have included support for something like this (ported from Structure and Interpretation of Computer Programs, second ed., p. 30):

/// <summary>Return the square root of x.</summary>
double sqrt(double x) {
  bool goodEnough(double guess) {
    return Math.Abs(square(guess) - x) < 0.001;
  }
  double improve(double guess) {
    return average(guess, x / guess);
  }
  double sqrtIter(double guess) {
    return goodEnough(guess) ? guess : sqrtIter(improve(guess));
  }
  sqrtIter(1.0);
}
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
Steve Betten
  • 463
  • 1
  • 5
  • 10

2 Answers2

36

In fact, C# has exactly this.

double sqrt(double x) {
    var goodEnough = new Func<double, bool>(guess =>
        Math.Abs(square(guess) - x) < 0.001
    );
    var improve = new Func<double, double>(guess =>
        average(guess, x / guess)
    );
    var sqrtIter = default(Func<double, double>);
    sqrtIter = new Func<double, double>(guess =>
        goodEnough(guess) ? guess : sqrtIter(improve(guess))
    );
    return sqrtIter(1.0);
}
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
yfeldblum
  • 65,165
  • 12
  • 129
  • 169
  • 1
    Yep, C# won't optimize tail-recursion into a loop. *That* feature is missing from the language. – yfeldblum Feb 23 '09 at 02:53
  • Thanks for pointing this out! I'll have to push for a switch to .NET 3.5 (we're inexplicably still using 2.0). – Steve Betten Feb 23 '09 at 02:54
  • 1
    While you're at it, you should push for a switch to Haskell. But .net-3.5 is good too! – yfeldblum Feb 23 '09 at 03:04
  • hmm why do you have that default thingy there and not initialize directly? – Johannes Schaub - litb Feb 23 '09 at 03:44
  • Because otherwise the name 'sqrtIter' wouldn't be available within the anonymous function. In order to do the recursion, the anonymous function needs a variable name to call. So we have to declare the variable `sqrtIter` first, and then assign the anonymous function to it. – yfeldblum Feb 23 '09 at 04:00
  • +1: This is such an eye-opening experience. Is this happened to be a way to write functionally? – dance2die Feb 23 '09 at 04:06
  • Yes, this is an example of an impure form of functional programming. While C# certainly has strong potential as a functional language, it is a little more verbose than other functional languages, such as F#, a functional .net language. – yfeldblum Feb 23 '09 at 04:40
  • @Steve Betten - You can use this style in C# 2.0; see my example. – configurator Feb 23 '09 at 09:04
  • Sorry the answer is wrong. C#'s lexical scopes are not truly lexically scoped. – leppie Feb 23 '09 at 09:41
  • 1
    @Steve C#2 has inner anonymous functions; I wasn't sure it had closures. @leppie it has true *lexically* scoped closures in that the *names* of variables are closed over, not their *values*. (That's the most common complaint, anyway. If yours is different, please explain.) – yfeldblum Feb 23 '09 at 10:45
8

Like Justice said, you can do it with C# 3.5 and lambdas; if you have C# 2.0, you can use anonymous functions, although it would be somewhat less sexy:

double sqrt(double x) {
    Func<double, bool> goodEnough = delegate(double guess) {
        return Math.Abs(square(guess) - x) < 0.001;
    };
    Func<double, double> improve = delegate(double guess) {
        return average(guess, x / guess);
    };
    Func<double, double> sqrtIter = null;
    sqrtIter = delegate(double guess) {
        return goodEnough(guess) ? guess : sqrtIter(improve(guess));
    };
    return sqrtIter(1.0);
}

Edit: I forgot, Func isn't defined in C# 2.0, so you have to define it yourself:

 public delegate TResult Func<T, TResult>(T guess);
configurator
  • 40,828
  • 14
  • 81
  • 115