【问题标题】:Inferring generic types with functional composition推断具有功能组合的泛型类型
【发布时间】:2011-05-22 19:17:28
【问题描述】:

假设我想实现一个函数式组合,像这样:

    public Func<T,T> Compose<T>(Func<T,T> f, Func<T,T> g)
    {
        return new Func<T,T>( x => f(g(x)));
    }

现在在实践中,我可以像这样使用 Compose() fn:

    public String ToUpper(String s) { return s.ToUpper(); }        
    public String Replicate(String s) { return s+s; }

    public void Run()
    {
        var h = Compose<String>(ToUpper, Replicate);
        System.Console.WriteLine("{0}", h("fred"));
    }

结果是FREDFRED

有没有办法使用更简单的语法来调用 Compose?我试过这样:

        var h = Compose(ToUpper, Replicate);

...但我得到一个编译错误:

错误 CS0411:无法从用法中推断方法“FunctionalTest.Compose(System.Func, System.Func)”的类型参数。尝试明确指定类型参数。

很好理解。我想知道是否有可能以不同的方式声明它并让推论真正起作用。


编辑
问题的根源:我正在观看本科生函数式编程课程的在线讲座,加州大学伯克利分校的 CS61A。 (在 youtube 上找到)。我没有接受过任何正式的 FP 培训,我想我可能会学到一些东西。教授使用scheme,他谈到scheme + lisp是纯粹的函数式语言,而其他语言则不然。他特别指出 Pascal、C、C++ 和 Java(但不是 C#)缺乏函数式能力,并表示很难用这些语言进行函数式组合(“没有站在你的头上”)。他断言指向函数的指针(在 C、C++ 中可用)与函数“实体”不同,即 lambda。我明白了。

有趣 - 他没有提到 Javascript 或 C#,我认为它们都是主流语言,它们都具有很好的功能。 (我不知道 F#。)

我觉得奇怪的是,这是去年(14 个月前)的演讲,但他似乎不了解主流现代语言的功能方面。

所以我一直在做练习,但我没有使用 scheme 或 lisp,而是使用 C#。并且还用 Javascript 做其中的一些。

感谢大家的高质量回复。

【问题讨论】:

  • 切向提示:您可以像这样进一步概括您的 Compose 方法:paste.pocoo.org/show/393393
  • 函数组合没有更通用的类型吗? Func&lt;A,C&gt; Compose&lt;A,B,C&gt;(Func&lt;B,C&gt; f, Func&lt;A,B&gt; g)(翻译自 Haskell (.) :: (b -&gt; c) -&gt; (a -&gt; b) -&gt; a -&gt; c)。

标签: c# functional-programming


【解决方案1】:

下面的代码可以工作:

Func<string, string> toUpper = ToUpper;
Func<string, string> replicate = Replicate;

// now the compiler knows that the parameters are Func<string, string>
var h = Compose(toUpper, replicate);

所以也许您仍然可以通过仅定义一次这些变量并在整个测试中重用它们来获得您正在寻求的可读性改进(我假设这是一个测试实用程序......)

【讨论】:

  • 这不是一个专门的测试实用程序,我只是“获得功能”,仅此而已。我已经将这种东西用于性能测试工具,但这是独立的。
【解决方案2】:

我喜欢 Ran 的回答 (+1),但我认为这使它更加简洁和美观。 (假设您可以按如下方式重新定义函数。)

Func<string, string> toUpper = s => s.ToUpper();
Func<string, string> replicate = s => s + s;

var h = Compose(toUpper, replicate);

【讨论】:

    【解决方案3】:

    添加到 lasseespeholt 的答案,如果您将 Compose 定义为扩展方法(重命名为“Then”以便结果更有意义):

    public static Func<T, T> Then<T>(this Func<T, T> f, Func<T, T> g)
    {
        return x => g(f(x));
    }
    

    你可以说得流利:

    var h = toUpper.Then(replicate); // .Then(trim) etc...
    

    【讨论】:

      【解决方案4】:

      您也可以传递Compose 参数,并让它实际评估函数;在这种情况下,它应该能够推断出参数类型。 (不过,您可能仍需要指定返回类型。)

      除此之外,不,没有办法在 C# 中推断出这样的事情。

      【讨论】: