递归执行列表中的func

时间:2010-11-14 20:20:16

标签: c# linq recursion functional-programming

给定一个Func<string, string>列表,是否可以编写一个循环遍历列表并返回结果的语句:

string result = f1(f2(f..(input));

我有以下代码(有效),但我对临时变量不满意。

public static string WrapEachElementWith<T>
    (   this IEnumerable<T> target, 
        params Func<string, string>[] func )
{
    string result = string.Empty;
    target.Each(s =>
                    {
                        var tmp = s.ToString();
                        func.Reverse().Each(x => tmp = x(tmp));
                        result += tmp;
                    });
    return result;
}

如何简化/重构?

更新: 我应该提供更多背景资料。在看到高阶JavaScript会话和John在Oredev的滥用c#会话后,我正在使用c#中的函数编程。

目的是生成html。

var TABLE = WrapWith("TABLE");
var TR = WrapWith("TR");
var TD = WrapWith("TD");
const string expected = "<TABLE><TR><TD>1</TD></TR><TR><TD>2</TD></TR></TABLE>";

var result = TABLE(stringArray.WrapEachWith(TR, TD));
result.ShouldEqual(expected);

static Func<String, String> WrapWith(string element)
{
    var startTag = '<' + element + '>';
    var endTag = "</" + element + '>';
    return s => startTag + s + endTag;
}

3 个答案:

答案 0 :(得分:3)

在我看来,你做了四件事:

  • 将每个项目转换为字符串
  • 依次应用这些功能
  • 将该复合函数应用于序列中的每个字符串
  • 将结果加在一起(效率低下)

我会将这四个方面分开 - 特别是string.Join对第四部分有效,Enumerable.Select做第三部分。

我也会避免颠倒操作的顺序 - 我希望我指定的第一个操作是第一个应用的操作,个人。

所以,我会将此方法重写为 return 一个Func<string, string>,然后可以与SelectJoin一起使用。例如:

public static Func<string, string> Compose(params Func<string, string> funcs)
{
    return input => {
        string current = input;
        foreach (var func in funcs)
        {
            current = func(current);
        }
        return current;
    };
}

当然,您可以使用以下标记来制作此通用名称:

public static Func<T, T> Compose(params Func<T, T> funcs)

然后您可以使用以下内容调用它:

var composite = Compose<string>(FirstFunction, SecondFunction, ThirdFunction);

var query = string.Join("", items.Select(x => x.ToString())
                                 .Select(composite));

答案 1 :(得分:1)

public static string WrapEachElementWith
    (   string input, 
        params Func<string, string>[] func )
{
    foreach (var f in func.Reverse())
        input = f(input);
    return input;
}

不确定为什么需要模板参数,所有函数都将字符串映射到字符串,对吧?

请注意,Each没有IEnumerable个扩展名,因此您必须求助foreach或撰写自己的Each

编辑:
您的代码实际上将此函数应用于列表中的所有值,因此实际代码将类似于:

public static string F<T>
    (   this IEnumerable<T> target, 
        params Func<string, string>[] func )
{
    target.Select(item => WrapEachElementWith(item.ToString(), func))
          .Aggregate((sum, cur) => sum + cur);
}

正如@Jon已经提到的那样,总结这种方式是非常低效的,因此你可能想这样说:

string.Join("", target.Select(
                  item => WrapEachElementWith(item.ToString(), func)));

答案 2 :(得分:0)

This guy使用LINQ编写了一个完整的光线跟踪器。我没有密切关注他的代码,但他描述了使用一种称为“Y-combinator”的技术在LINQ语句中创建递归。他引用了this blog posting,它详细描述了这些递归的lambda表达式。

我不知道这是不是你想要的,但它可能会让你站稳脚跟。