我正在尝试弄清如何在一行中编写递归函数(例如阶乘,尽管我的函数要复杂得多)。为了做到这一点,我想使用Lambda演算的Y组合子。
以下是第一个定义:
Y = λf.(λx.f(x x))(λx.f(x x))
这里是简化后的定义:
Y g = g(Y g)
我试着用C#写成了这样:
// Original
Lambda Y = f => (new Lambda(x => f(x(x)))(new Lambda(x => f(x(x)))));
// Reduced
Lambda Y = null; Y = g => g(Y(g));
(Lambda
是一个 Func<dynamic, dynamic>
。我最初尝试使用 using
进行类型定义,但那并不起作用,所以现在它是用 delegate dynamic Lambda(dynamic arg);
定义的)
我的阶乘 lambda 看起来像这样(从这里改编):
Lambda factorial = f => new Lambda(n => n == 1 ? 1 : n * f(n - 1));
我这样调用:
int result = (int)(Y(factorial))(5);
然而,在两种情况下(Y组合子的原始形式和简约形式),我都遇到了堆栈溢出异常。从使用简约形式的结果可以推断,它似乎只是最终调用了Y(factorial(Y(factorial(Y(factorial(...
,并且永远不会真正进入阶乘lambda函数。由于我没有太多调试C# lambda表达式的经验,并且我肯定不理解lambda演算的大部分内容,因此我不知道发生了什么或如何解决它。
如果有关系的话,这个问题是受尝试编写一个在C#中回答这个问题的一行代码所启发的。
我的解决方案如下:
static IEnumerable<string> AllSubstrings(string input)
{
return (from i in Enumerable.Range(0, input.Length)
from j in Enumerable.Range(1, input.Length - i)
select input.Substring(i, j))
.SelectMany(substr => getPermutations(substr, substr.Length));
}
static IEnumerable<string> getPermutations(string input, int length)
{
return length == 1 ? input.Select(ch => ch.ToString()) :
getPermutations(input, length - 1).SelectMany(
perm => input.Where(elem => !perm.Contains(elem)),
(str1, str2) => str1 + str2);
}
// Call like this:
string[] result = AllSubstrings("abcd").ToArray();
我的目标是编写一个一行式的自递归 lambda 函数 getPermutations
,以便将其插入到 AllSubstrings
的 SelectMany
中,并让 AllSubstrings
成为一行式。
我的问题如下:
- 在 C# 中是否可能使用 Y 组合子?如果是,则我在实现中做错了什么?
- 如果在 C# 中可以使用 Y 组合子,那么我如何使字符串子串问题的解决方案(
AllSubstrings
函数)成为一行代码? - 无论 Y 组合子是否在 C# 中可行,是否有其他编程方法可以使
AllSubstrings
成为一行代码?
Y g = g(Y g)
才是有效的。在急切求值时,解决方法是使用 eta-扩展:Y g = g (\x-> (Y g) x)
。或者也许Y g x = g (\x-> (Y g) x) x
更容易理解。 - Will NessLambda y = null; y = g => g(new Lambda(x => (y(g))(x)));
时,它起作用了。好吧,我想这回答了第一个问题。 - JashaszunconcatMap permutations . sequences
,其中sequences (x:xs) = [a | b<-sequences xs, a<-[x:b,b] ] ; sequences [] = [[]]
,而permutations
是一个标准函数。 - Will Ness