函数式C# API设计(返回函数参数并增加计算结果)

发布于 2024-07-13 10:29:20 字数 1434 浏览 6 评论 0原文

有一个关于 C# 代码中函数式编程技术的使用的问题。 示例

让我们有接口

interface IGraph { /*contains vertices and edges*/}

假设我们需要布局图的顶点(为每个顶点分配 Point)。

interface ILayoutInfo {
  Point GetVertexPoint(vertex);
}

简单的布局路线可以有这样的签名:

ILayoutInfo SimpleLayout(IGraph graph);

可以这样使用

void Demo() {
  IGraph graph = CreateGraphInAnyWay();
  ILayoutInfo layout = SimpleLayout(graph);
  PrintCoordinates(graph,layout);
}

在下面的设计中 PrintCoordinates 需要对图形和布局的引用。

考虑功能风格设计,其中布局路由增强图形信息 有关图顶点坐标的信息。

ILayoutedGraph SimpleLayoutNew(IGraph graph);

其中 ILayoutedGraph 实现 IGraph 和 ILayoutInfo

void DemoNew() {
  IGraph graph = CreateGraphInAnyWay();
  ILayoutedGraph layoutedGraph = SimpleLayoutNew(graph);
  PrintCoordinatesNew(layoutedGraph);
}

1) 在此设计中,PrintCooperativesNew 仅获取一个参数。 2)奇怪的接口ILayoutedGraph诞生了,它不包含任何方法,只是 包装其他接口。 如果某个库有其他类型,如 INetwork、ITree,我们最终会得到 创建包装接口 ILayoutedNetwork、ILayoutedTree (这很糟糕)。

因此,这种技术仅在函数语言中使用,只是因为它们不能以其他方式工作(没有状态,因此函数必须将输入与计算信息结合起来以供外部例程使用),或者它在命令式世界中也可行?

非常感谢,

PS:可以在这里找到更详细、漂亮的打印示例 http://tivadj.blogspot.com/ 2009/02/designing-c-api-in-function-style.html

There is a question regarding usage of functional programming techiques in C# code. Example

Let we have interface

interface IGraph { /*contains vertices and edges*/}

Suppose we need to layout graph's vertices (assign Point to each vertex).

interface ILayoutInfo {
  Point GetVertexPoint(vertex);
}

Simple layout route can have such signature:

ILayoutInfo SimpleLayout(IGraph graph);

Which can be used in such way

void Demo() {
  IGraph graph = CreateGraphInAnyWay();
  ILayoutInfo layout = SimpleLayout(graph);
  PrintCoordinates(graph,layout);
}

In design below PrintCoordinates need both references to graph and layout.

Consider functional style design where layouting routing augments graph information
with information about graph vertices coordenates.

ILayoutedGraph SimpleLayoutNew(IGraph graph);

Where ILayoutedGraph implements BOTH IGraph and ILayoutInfo

void DemoNew() {
  IGraph graph = CreateGraphInAnyWay();
  ILayoutedGraph layoutedGraph = SimpleLayoutNew(graph);
  PrintCoordinatesNew(layoutedGraph);
}

1)In this design PrintCoordinatesNew gets only ONE parameter.
2)Weird interface ILayoutedGraph was born which doesn't contain any methods and just
wraps other interfaces. If some library has other types like INetwork, ITree we end up
creating wrap interfaces ILayoutedNetwork, ILayoutedTree (which is bad).

So such techique is used only in functinal languages just because they can't work in other way (there are no state so function must combine input with calculated info to be used by outer routines) or it is viable in imperative world too?

Thanks a lot,

PS: a more verbose pretty printed example can be found here
http://tivadj.blogspot.com/2009/02/designing-c-api-in-functional-style.html

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

拥有 2024-07-20 10:29:20

如果传递两个参数时让您感到不安的是您的布局(ILayoutInfo)链接到用于生成它的图形。 我传递带有未用于生成布局的图形的布局是没有意义的。

在这种情况下,您可以在布局信息中保留对图形的引用,并在 ILayoutInfo 接口上提供访问器。

这样,您只能传递 ILayoutInfo 实例,而您的 PrintCooperatives 函数仍然可以访问用于生成 ILayoutInfo 的图形。

如果您有其他类型的对象可以生成布局信息,请为它们使用通用接口,或使 ILayoutInfo 通用。

If seems that what disturbs you when passing two arguments is that your layout (ILayoutInfo) is linked to the graph used to generate it. I would be meaningless to pass a layout with a graph that was not used to generate it.

In this case, you can keep a reference to the graph in the layout info, and provide an accessor on the ILayoutInfo interface.

This way, you can only pass the ILayoutInfo instance, and you PrintCoordinates function can still acces the graph that was used to generate the ILayoutInfo.

If you have other kind of object that can generate layout infos, use a common interface for them, or make the ILayoutInfo generic.

苍风燃霜 2024-07-20 10:29:20

您可以使用实现两个接口并将返回值转换为该类的类来实现此目的吗?

Can you implement this using a class that implements both interfaces and casting the returns as that class?

沫尐诺 2024-07-20 10:29:20

你原来的API没有问题,

void Demo() {
  IGraph graph = CreateGraphInAnyWay();
  ILayoutInfo layout = SimpleLayout(graph);
  PrintCoordinates(graph,layout);
}

功能完美。 命令式 API 在创建后会涉及到对graph 的更改。 例如,

void Demo() {
  IGraph graph = CreateGraphInAnyWay();
  graph.Layout(new SimpleLayout()); // changes the state of graph
  PrintCoordinates(graph);
}

对于 ILayoutedGraph、ILayoutedTree、ILayoutedQueue 等问题,我认为您可以通过函数式语言中的泛型类和 OO 语言中允许的多重继承来解决这个问题。

就我个人而言,我会推荐泛型: ILayout 其中 a 是需要布局的边缘的东西。

There is no problem with your original API of

void Demo() {
  IGraph graph = CreateGraphInAnyWay();
  ILayoutInfo layout = SimpleLayout(graph);
  PrintCoordinates(graph,layout);
}

It is perfectly functional. An imperative API would involve a change to graph once it is created. Eg

void Demo() {
  IGraph graph = CreateGraphInAnyWay();
  graph.Layout(new SimpleLayout()); // changes the state of graph
  PrintCoordinates(graph);
}

As for the problem of ILayoutedGraph, ILayoutedTree, ILayoutedQueue, etc, I think you would get around this by generic classes in a functional languages and multiple inheritance in the OO languages where this is allowed.

Personally, I would recommend generics: ILayout<a> where a is something with edges that needs to be laid out.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文