如何在C#中编写curried方法?

时间:2016-03-07 11:09:04

标签: c# functional-programming currying

以下代码将运行并打印11

using System;
class MainClass {
  public static void Main (string[] args) {
    Func<int, int, int> _Add = (x,y) => x + y;
    var Add = Curry(_Add);
    int z = Add (5) (6);
    Console.WriteLine(z); // 11
  }

  public static Func<T1, Func<T2, T3>> Curry<T1, T2, T3>(Func<T1, T2, T3> f) {
    return x => y => f(x, y);
  }
}

但我想知道是否可以在课堂上定义咖喱方法而不是在本地

public static ... Add(...) { /* define curried form using Curry helper */
  return f(x, y); 
}

它应该像这样可以调用

public static void Main(string[] args) {
  Add (5) (6); // => should return 11
}

如果我有很多方法需要咖喱,那么使用辅助函数来定义它们会很不错。手工编写每一个都太累人了。无论如何,我无法弄清楚这个的正确语法。请帮忙。我是C#noob。

4 个答案:

答案 0 :(得分:3)

您只需从Func<int, int>

返回Add即可
public static Func<int, int> Add(int x) { 
    return y => x + y;
}

或将其声明为财产:

public static Func<int, Func<int, int>> Add
{
    get { return x => y => x + y; }
}

答案 1 :(得分:1)

使用原始咖喱构造函数可以初始化函数。您需要多大的灵活性?

CASE

对于一堆方法实现,它们可以像这样包含

update Employee 
set Gender = (
    case Gender when 'M' then 'F'
    when 'F' then 'M' 
    else Gender end
);

答案 2 :(得分:0)

要首先解决函数,首先需要将函数传递给函数。

//Example of what I have to to right now.

public class Weapon
{
    public string m_name;
    public int m_attack_damage;
    // ETC...
}


//This is an example class that will contain game logic handling Actor
//currently.. Because I have a type that is not regonized by the Sqlite Database
//it will not create a table/field for this class as a type due to this without a ModelClass wrapper to convert those values.
public class Actor
{
    public string m_Name { get; set; } //This translates perfectly to the SQLite database.
    public int m_Age { get; set; }     //This translates perfectly to the SQLite database.

    //THIS WILL NOT TRANSLATE TO THE SQLite DB without a Model Wrapper.
    public Weapon m_Weapon { get; set; } //This is a type of a custom class called Weapon. 

    //This is the model class I need to pass to the Table/Field to be translated correctly.
    public ActorDBModel ToDBModel()
    {
        var model = new ActorDBModel();

        model.Name = m_Name;
        model.Age = m_Age;
        model.Weapon_Name = m_Weapon.m_Name;
        model.Weapon_Attack_Damage = m_Weapon.m_attack_damage;
        return model
    }

    //ETC...
}

//THIS IS WHAT I HAVE TO CREATE IN ORDER TO HANDLE THE ABOVE CLASS

public class ActorDBModel
{
    public string Name {get; set;}
    public string Age {get; set;}
    public string Weapon_Name {get; set;} //These are set from the Actor
    public int Weapon_Attack_Damage {get; set;} // These are set from the ActorDBModel

    //ETC...
}

答案 3 :(得分:0)

理想情况下,您需要进行currying和部分应用,当您有许多参数时,currying变得非常低效。当我这样做时,我实现了大量名为currypar的静态方法,它们最多可以处理10个参数并部分应用N个值。

以下是4个参数的示例:

public static Func<T1, Func<T2, Func<T3, Func<T4, R>>>> curry<T1, T2, T3, T4, R>(Func<T1, T2, T3, T4, R> f) =>
        (T1 a) => (T2 b) => (T3 c) => (T4 d) => f(a, b, c, d);

很快就会有很多嵌套的委托。这就是部分应用程序的用武之地:

public static Func<T4, R> par<T1, T2, T3, T4, R>(Func<T1, T2, T3, T4, R> func, T1 a, T2 b, T3 c) =>
        (T4 d) => func(a, b, c, d);

要使用上面的curyring示例,您可以这样做:

var f = curry(func);
var r = f(1)(2)(3)(4);

但通常部分应用是你想要的:

var f = par(1,2,3);
var r = f(4);

这完全在我的C#功能库中实现:enter image description here

language-ext

Currying source

您可以按原样获取这些源文件,没有外部依赖项。

在您的示例中,您可以声明Add方法:

class Test
{
    public static readonly Func<int, Func<int, int>> Add => x => y => x + y;
}

或者经典方式并使用curry函数:

class Test
{
    public static readonly int Add(int x, int y) => x + y;
}

var add = curry(Test.Add);
var r = add(1)(2);

或部分适用:

class Test
{
    public static int Add(int x, int y) => x + y;
}

var addOne = par(Test.Add, 1);
var r = addOne(2);