C#是一个带有一个类型为T的参数的函数,它返回一个带有一个类型为T的参数的函数,但实际上需要更多的参数

时间:2016-07-06 19:13:38

标签: c# recursion functional-programming delegates

我有一个Dictionary<T, IEnumerable<T>>来编码一棵树: 键表示树的所有节点,值是相应节点的子节点。 如果节点没有子节点,则其值为空可枚举。

E.g。以下映射treeMapT = int将是一个编码树的映射:

treeMap[1] = { 2, 3, 4 }
treeMap[2] = { 5, 6 }
treeMap[3] = { 7 }
treeMap[4] = { }
treeMap[5] = { }
treeMap[6] = { }
treeMap[7] = { 8 }
treeMap[8] = { }

我想编写一个方法UseFunctionOnTree(T node, Dictionary<T, IEnumerable<T>> treeMap, Function F),它将F apllies到给定节点,从旧newFF获取一个新函数node ,并将newF适用于所有儿童。

据我所知:

public class MapHelper<T>
{
    public delegate Function Function (T element);

    public static void UseFunctionOnTree(T node,
                           Dictionary<T, IEnumerable<T>> treeMap, Function F)
    {
        Function newF = F(node);
        foreach (T child in treeMap[node])
            UseFunctionOnTree (child, treeMap, newF);
    }
}

现在我的问题是我不知道如何定义这样的Function。 我可以这样定义:

public Function Useless(T element)
{
    DoSthWith(element);
    return Useless;
}

但我不知道如何定义一个返回其他东西的函数!

我的一个用例如下: 我有一个MyObject类型的树,其中MyObject可能如下所示:

public class MyObject
{
    public int index;
}

我采用了一些int - 值偏移,我们说firstOffset = 3。我希望Functionoffset添加到node(即index)。然后我想为node的所有子项添加偏移量,但这次偏移量应为nextOffset = firstOffset + node.index

这是我想要的伪代码:

public Function AddOffset(T element)
{
    int firstOffset;
    // somehow make firstOffset = 3

    int newOffset = firstOffset + element.index;
    element.index = newOffset;
    return AddOffset // but this time with the new offset
}

我该怎么做?我觉得这可能是一些lambda声明可以解决的......

我需要更多参数吗?从直观的角度来看,我没有。 例如。我们有

node1 node1.index = 1

node2node2.index = 100

我们将F作为 “将3添加到参数的索引并将该数字存储为n。返回一个类似于此的函数,但将n添加到其参数的索引中。”

由此我们得到F(node1)会将node1.index更改为3+1=4并将4添加到其参数的索引中,因此(F(node1))(node2)将更改node2.index转到4+100=104并将104添加到其参数的索引中。 ((F(node1))(node2))(node1)会将node1.index更改为104+4=108并将108添加到其参数的索引中,依此类推。

此外,我还要注意,我并不总是需要/更改一个int参数。可能是我需要几个参数,它们的类型也可以根据我想要解决的具体问题而变化。因此,如果我不必事先决定需要多少(以及哪种类型)参数,那将是很好的。它应该全部包含在Function本身。

任何提示都表示赞赏!

2 个答案:

答案 0 :(得分:0)

有一种方法可以做到这一点,但我不推荐它。请再考虑一下您使用的算法和数据结构。就个人而言,我会使用更面向对象的方法,但让我们尝试一下。

第一个抽象是表示使用另一个泛型类型Q进行操作的数据:

public class MapHelper<T, Q> {
     // (insert the following code here)
}

首先,我们需要更多代表来表示您的映射:

    public delegate Q Getter(T element);
    public delegate void Setter(T element, Q value);
    public delegate Q Operation(Q left, Q right);

Getter允许我们检索索引,Setter可以编写新值,Operation这里是一个简单的添加。在我们应用映射函数时,我们需要这个函数:

    public static void UseFunctionOnTree(T node,
                           Dictionary<T, IEnumerable<T>> treeMap, Function F, Getter g, Setter s) {
        Function newF = F(node, g, s);
        if (!treeMap.ContainsKey(node)) return;
        foreach (T child in treeMap[node])
            UseFunctionOnTree(child, treeMap, newF, g, s);
    }

现在我们可以定义使用声明的委托的GenericAddOffset闭包来定义一个新的lambda函数:

    public static Function GenericAddOffset(T element, Getter g, Setter s, Operation o, Q left) {
        Q newOffset = o(left, g(element));
        s(element, newOffset);
        return (T element1, Getter g1, Setter s1) => {
            return GenericAddOffset(element1, g1, s1, o, newOffset);
        };
    }

这是您缺少的链接:闭包允许您使用外部作用域中的变量来定义函数。

最后,我定义了一个辅助函数:

    public static void PrintTree(Dictionary<T, IEnumerable<T>> tree, Getter g) {
        foreach (var value in tree) {
            Console.Write("[" + g(value.Key).ToString() + "]: ");
            foreach (var node in value.Value)
                Console.Write(g(node).ToString() + " ");
            Console.WriteLine();
            Console.WriteLine();
        }
    }

现在,用:

using Helper = MapHelper<MyObject, int>;

我们可以编写以下示例程序:

class Program {

    public static void Main(string[] args) {
        var tree = new Dictionary<MyObject, IEnumerable<MyObject>>();
        Helper.Getter g = t => (int)t.index;
        Helper.Setter s = (t, o) => { t.index = o; };

        var root = new MyObject();
        var node1 = new MyObject();
        var node2 = new MyObject();
        s(root, 1);
        s(node1, 1);
        s(node2, 100);
        tree.Add(root, new[] { node1, node2 });

        Helper.PrintTree(tree, g);

        Helper.UseFunctionOnTree(root, tree,
            (t, g1, s1) => { return Helper.GenericAddOffset(t, g1, s1, (x, y) => x + y, 3); }, g, s);

        Helper.PrintTree(tree, g);
        Console.ReadLine();
    }
}

输出

[1]: 1 100

[4]: 5 104

答案 1 :(得分:0)

只需定义一个辅助功能

public static Function MakeAddOffset(int offset)
{
    return element => {
        int newOffset = offset + element.index;
        element.index = newOffset;
        return MakeAddOffset(newOffset);
    };
}

然后使用MakeAddOffset(3)作为AddOffset函数。 (您可以通过以与AddOffset相同的方式定义类似的辅助函数F,将其从MakeF推广到任何函数MakeAddOffset。)