I was playing with recursive lambdas in C# and have found two approaches to do this on the web. One approach uses fixed point combinator and the other does not. In the code below f1 is built using combinator, and f2 is defined directly. My question is, do we need fixed point combinators in C# or the language already provides all we need, so we can leave them alone?
我正在使用C#中的递归lambdas,并且发现了两种在网络上执行此操作的方法。一种方法使用定点组合器而另一种方法不使用。在下面的代码中,使用组合器构建f1,并直接定义f2。我的问题是,我们是否需要在C#中使用定点组合器,或者语言已经提供了我们所需要的所有内容,所以我们可以将它们单独留下来?
class Program
{
static Func<T, T> F<T>(Func<Func<T,T>,Func<T,T>> f)
{
return x => f(F(f))(x);
}
static void Main(string[] args)
{
Func<Func<int,int>,Func<int,int>> f = fac => x => x == 0 ? 1 : x * fac(x - 1);
var f1 = F(f);
Console.WriteLine(f1(5));
Func<int, int> f2 = null;
f2 = x => x == 0 ? 1 : x * f2(x - 1);
Console.WriteLine(f2(5));
}
}
4 个解决方案
#1
As we can give a name to a method, that means that the language already has the necessary support for recursion built into it.
因为我们可以为方法命名,这意味着该语言已经为内置的递归提供了必要的支持。
Note that the second method given in your question involves changing the value of a variable after it has been introduced, making it not "pure" functional programming. Y-combinator is only necessary if your system of functional calculus doesn't have a built-in notion of a function that can refer to its own definition by name before the definition is completely defined. C# has two ways to do that directly: 1. initially defining the function variable as null and 2. declaring an ordinary named method (by far the preferred technique).
请注意,您的问题中给出的第二种方法涉及在引入变量后更改变量的值,使其不是“纯粹的”函数式编程。 Y-combinator仅在您的函数微积分系统没有函数的内置概念时才需要,该函数可以在定义完全定义之前通过名称引用其自己的定义。 C#有两种直接方法:1。最初将函数变量定义为null,2。声明一个普通的命名方法(到目前为止是首选技术)。
#2
I think it can be rather elegant with an extra utility class called Recursive as follows:
我认为使用名为Recursive的额外实用程序类可以相当优雅,如下所示:
public static class Recursive {
public static Func<R> Func<R>(
Func<Func<R>, Func<R>> f) {
return () => f(Func(f))(); }
public static Func<T1, R> Func<T1, R>(
Func<Func<T1, R>, Func<T1, R>> f) {
return x => f(Func(f))(x); }
public static Func<T1, T2, R> Func<T1, T2, R>(
Func<Func<T1, T2, R>, Func<T1, T2, R>> f) {
return (a1, a2) => f(Func(f))(a1, a2);
}
//And so on...
}
class Program {
static void Main(string[] args) {
Console.WriteLine(
Recursive.Func<int, int>(factorial =>
x => x == 0 ? 1 : factorial(x - 1) * x
)
(10)
);
Console.WriteLine(
Recursive.Func<int,int,int>(gcd =>
(x,y) =>
x == 0 ? y:
y == 0 ? x:
x > y ? gcd(x % y, y):
gcd(y % x, x)
)
(35,21)
);
}
}
#3
Another alternative is to declare your recursive Func delegates as static members:
另一种方法是将递归Func委托声明为静态成员:
static Func<int, int> Factorial = (n) => n <= 1 ? 1 : n*Factorial(n - 1);
#4
What does "need" mean? C# doesn't need them, as you shouldn't be attempting this sort of functional programming in C#. It's just a pathway to pain.
“需要”是什么意思? C#不需要它们,因为你不应该在C#中尝试这种函数式编程。这只是痛苦的途径。
Memoizing a recursive function is one place you'd want a fixed point combinator. Compare this in C# to Haskell.
记住递归函数是一个你想要一个定点组合器的地方。将C#中的这个与Haskell进行比较。
So, before C# "needs" this, it has a needs a lot of work to make this sort of programming reasonably practical.
因此,在C#“需要”之前,需要做大量的工作才能使这种编程变得合理。
#1
As we can give a name to a method, that means that the language already has the necessary support for recursion built into it.
因为我们可以为方法命名,这意味着该语言已经为内置的递归提供了必要的支持。
Note that the second method given in your question involves changing the value of a variable after it has been introduced, making it not "pure" functional programming. Y-combinator is only necessary if your system of functional calculus doesn't have a built-in notion of a function that can refer to its own definition by name before the definition is completely defined. C# has two ways to do that directly: 1. initially defining the function variable as null and 2. declaring an ordinary named method (by far the preferred technique).
请注意,您的问题中给出的第二种方法涉及在引入变量后更改变量的值,使其不是“纯粹的”函数式编程。 Y-combinator仅在您的函数微积分系统没有函数的内置概念时才需要,该函数可以在定义完全定义之前通过名称引用其自己的定义。 C#有两种直接方法:1。最初将函数变量定义为null,2。声明一个普通的命名方法(到目前为止是首选技术)。
#2
I think it can be rather elegant with an extra utility class called Recursive as follows:
我认为使用名为Recursive的额外实用程序类可以相当优雅,如下所示:
public static class Recursive {
public static Func<R> Func<R>(
Func<Func<R>, Func<R>> f) {
return () => f(Func(f))(); }
public static Func<T1, R> Func<T1, R>(
Func<Func<T1, R>, Func<T1, R>> f) {
return x => f(Func(f))(x); }
public static Func<T1, T2, R> Func<T1, T2, R>(
Func<Func<T1, T2, R>, Func<T1, T2, R>> f) {
return (a1, a2) => f(Func(f))(a1, a2);
}
//And so on...
}
class Program {
static void Main(string[] args) {
Console.WriteLine(
Recursive.Func<int, int>(factorial =>
x => x == 0 ? 1 : factorial(x - 1) * x
)
(10)
);
Console.WriteLine(
Recursive.Func<int,int,int>(gcd =>
(x,y) =>
x == 0 ? y:
y == 0 ? x:
x > y ? gcd(x % y, y):
gcd(y % x, x)
)
(35,21)
);
}
}
#3
Another alternative is to declare your recursive Func delegates as static members:
另一种方法是将递归Func委托声明为静态成员:
static Func<int, int> Factorial = (n) => n <= 1 ? 1 : n*Factorial(n - 1);
#4
What does "need" mean? C# doesn't need them, as you shouldn't be attempting this sort of functional programming in C#. It's just a pathway to pain.
“需要”是什么意思? C#不需要它们,因为你不应该在C#中尝试这种函数式编程。这只是痛苦的途径。
Memoizing a recursive function is one place you'd want a fixed point combinator. Compare this in C# to Haskell.
记住递归函数是一个你想要一个定点组合器的地方。将C#中的这个与Haskell进行比较。
So, before C# "needs" this, it has a needs a lot of work to make this sort of programming reasonably practical.
因此,在C#“需要”之前,需要做大量的工作才能使这种编程变得合理。