【问题标题】:Recursively execute funcs in list递归执行列表中的函数
【发布时间】:2011-05-09 22:23:37
【问题描述】:

给定一个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;
}

【问题讨论】:

  • 递归? applyMany s [] = s 之类的东西; applyMany s (f:fs) = applyMany (f s) fs 在 C# 中?
  • 出于好奇,您使用递归的原因是什么?
  • 查看我的问题中的更新,我正在尝试使用要包装的元素列表将元素包装在 HTML 中
  • 我在问题更新中犯了一个错误,它现在有有效的代码。
  • 您不会以这种方式生成 HTML。就像您不手动解析 XML 一样,更不用说使用正则表达式了。这个问题太复杂了,已经解决了,你手动解决它。

标签: c# linq recursion functional-programming


【解决方案1】:

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

我不知道这是否正是您想要的,但它可能会让您走上正轨。

【讨论】:

    【解决方案2】:
    public static string WrapEachElementWith
        (   string input, 
            params Func<string, string>[] func )
    {
        foreach (var f in func.Reverse())
            input = f(input);
        return input;
    }
    

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

    请注意,IEnumerable 没有 Each 扩展名,因此您必须求助于 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)));
    

    【讨论】:

    • 每个输入都是T,ToString被调用一次开始。
    • @Jon:我以为每个target 的项目都称为一个。
    【解决方案3】:

    在我看来你正在做四件事:

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

    我会将这四个方面分开 - 特别是,string.Join 可以很好地用于第四部分,Enumerable.Select 可以用于第三部分。

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

    所以,我将把这个方法重写为 return 一个Func&lt;string, string&gt;,然后可以与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));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-17
      • 1970-01-01
      • 2017-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多