【问题标题】:Best way to generate a function that generates a function in C#生成在 C# 中生成函数的函数的最佳方法
【发布时间】:2012-02-17 12:03:01
【问题描述】:

F# 提供了一个特性,一个函数可以返回另一个函数。

在 F# 中生成函数的一个例子是:

let powerFunctionGenarator baseNumber = (fun exponent -> baseNumber ** exponent);

let powerOfTwo = powerFunctionGenarator 2.0;

let powerOfThree = powerFunctionGenarator 3.0;

let power2 = powerOfTwo 10.0;
let power3 = powerOfThree 10.0;

printfn "%f" power2;
printfn "%f" power3;

我能想到的在 C# 中实现相同目标的最佳方法是:

class Program
{
    delegate double PowerOf2(double exponent);
    delegate double PowerOf3(double exponent);
    delegate double PowerOfN(double n, double exponent);

    static void Main(string[] args)
    {
        PowerOfN powerOfN = (a, b) => { return Math.Pow(a,b) ; };
        PowerOf2 powerOf2 = (a) => { return powerOfN(2, a); };
        PowerOf3 powerOf3 = (a) => { return powerOfN(3, a); };

        double result = powerOf2(10);
        Console.WriteLine(result);
        result = powerOf3(10);
        Console.WriteLine(result);
    }
}

还有其他方法(/更好的方法)吗?

【问题讨论】:

  • 我从来没有在 C# 中做过这么多的柯里化,但 lambdas 不会限制柯里化的用处吗? @Ngm;您动态生成方法的原因是什么?
  • 没有什么特别的原因,只是想探索一下怎么做。
  • 请注意,所有函数都在 F# 中进行了柯里化,因此您可以等效地将 powerFunctionGenerator 写成 let powerFunctionGenarator baseNumber exponent = baseNumber ** exponent

标签: c# f# functional-programming


【解决方案1】:

如果您要问的是如何将powerFunctionGenarator 重写为 C#,您可以通过一种非常简单的方式来完成:

Func<double, double> powerFunctionGenarator(double baseNumber)
{
    return exponent => Math.Pow(baseNumber, exponent);
}

您不能将这样的方法声明放在 C# 中的另一个方法中。但是如果你想这样做,你可以在 lambda 中使用 lambda,正如 sblom 建议的那样:

Func<double, Func<double, double>> powerFunctionGenerator =
    baseNumber => exponent => Math.Pow(baseNumber, exponent);

相当于F#中的以下代码:

let powerFunctionGenarator = fun baseNumber -> (fun exponent -> baseNumber ** exponent)

【讨论】:

    【解决方案2】:

    当然,这在 C# 中很简单:

    using System;
    class P
    {
      static void Main()
      {
          Func<double, Func<double, double>> powerFunctionGenerator = 
              baseNumber => exponent => Math.Pow(baseNumber, exponent);  
    
          Func<double, double> powerOfTwo = powerFunctionGenerator(2.0);
          Func<double, double> powerOfThree = powerFunctionGenerator(3.0);
          double power2 = powerOfTwo(10.0); 
          double power3 = powerOfThree(10.0);
          Console.WriteLine(power2); 
          Console.WriteLine(power3);
      }
    }
    

    简单易懂。如果你不喜欢清单类型,大部分都可以替换为var

    【讨论】:

    • 您也可以将函数作为参数:Func&lt;double, Func&lt;double, double, double&gt;, Func&lt;double, double&gt;&gt; functionGenerator = (a, f) =&gt; b =&gt; f(a, b); var powOf2 = functionGenerator(2, PowerFunction);
    • 出于好奇,你有没有见过一些 C# 代码使用它来解决一些实际任务?我也用 C# 写了我的Curry/Uncurry 函数,但感觉语法太混乱了......
    • @TomasPetricek:当然;例如,我曾经写过一个 A* 寻路算法的实现。该算法通常需要一个可以估计两点之间距离的函数,但对于算法的任何特定运行,它需要一个可以估计任意点和一个固定点之间距离的函数。您可以使用部分应用程序来实现。
    • @EricLippert 这听起来绝对是偏函数应用的一个不错的应用。我不太相信在 C# 中使用它的原因是像 Point[] AStar(Point[] points, Func&lt;Point, Func&lt;Point, float&gt;&gt; metric) 这样的声明大约有 70 个字符长。对上网本不太友好:-)。
    【解决方案3】:

    您的原始 F# 到 C# 的几乎直译:

    Func<double,Func<double,double>> powerFunctionGenerator = 
        (baseNumber) => ((exponent) => Math.Pow(baseNumber, exponent));
    
    var powerOfTwo = powerFunctionGenarator(2.0);
    
    var powerOfThree = powerFunctionGenarator(3.0);
    
    var power2 = powerOfTwo(10.0);
    var power3 = powerOfThree(10.0);
    
    Console.WriteLine(power2);
    Console.WriteLine(power3);
    

    【讨论】:

      【解决方案4】:

      你可以写一个函数来柯里化另一个函数。不便之处在于您必须创建所需的所有重载。

      一个例子:

      using System;
      
      class Program {
      
          static Func<T2, TRes> Curry<T1, T2, TRes>(Func<T1, T2, TRes> f, T1 t1) {
              return (t2) => f(t1, t2);
          }
      
          static double PowerFunction(double d1, double d2) {
              return Math.Pow(d1, d2);
          }
      
          static void Main(string[] args) {
              var powerOf2 = Curry<double, double, double>(PowerFunction, 2);
              double r = powerOf2(3);
          }
      }
      

      【讨论】:

      • 我将不得不探索更多关于“咖喱”的信息。谢谢你的回答
      猜你喜欢
      • 2016-06-20
      • 1970-01-01
      • 2012-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-07
      • 2010-10-07
      • 1970-01-01
      相关资源
      最近更新 更多