我有一个Dictionary<T, IEnumerable<T>>
来编码一棵树:
键表示树的所有节点,值是相应节点的子节点。
如果节点没有子节点,则其值为空可枚举。
E.g。以下映射treeMap
与T = 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到给定节点,从旧newF
和F
获取一个新函数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
。我希望Function
将offset
添加到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
和
node2
与node2.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
本身。
任何提示都表示赞赏!
答案 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
。)