jay*_*jay 9 c# functional-programming
作为关于可重用库(对于我正在学习的)的FP设计的关键差异的前提之一,这些是更加以数据为中心的相应OO(通常).
这似乎也得到了TFD(Type-First-Development)等新兴技术的证实,Tomas Petricek在这篇博客文章中对此进行了很好的解释.
如今语言是多范式的,Petricek在其书中解释了可用于C#的各种功能技术.
我在这里感兴趣,因此问题是如何正确地划分代码.
所以我已经定义了库数据结构,使用相当于被区分的联合(如Petricek书中所示),并且我计划根据我的需求的域逻辑将它们与不可变列表和/或元组一起使用.
我在哪里放置对数据结构起作用的操作(方法......函数)?
如果我想要定义一个使用标准委托中包含的函数值的高阶函数,Func<T1...TResult>我在哪里放置它?
常识告诉我要在静态类中对这些方法进行分组,但我希望得到已经在C#中编写函数库的人的确认.
假设这是正确的,我有一个这样的高阶函数:
static class AnimalTopology {
IEnumerable<Animal> ListVertebrated(Func<Skeleton, bool> selector) {
// remainder omitted
}
}
Run Code Online (Sandbox Code Playgroud)
如果选择脊椎动物有N个我希望在库中暴露的特殊情况,那么暴露它们的方法更正确.
static class VertebratedSelectorsA {
// this is compatible with "Func<Skeleton, bool> selector"
static bool Algorithm1(Skeleton s) {
//...
}
}
Run Code Online (Sandbox Code Playgroud)
要么
static class VertebratedSelectorsB {
// this method creates the function for later application
static Func<Skeleton, bool> CreateAlgorithm1Selector(Skeleton s) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
任何迹象将非常感激.
编辑:
我想引用来自Mads Torgersen的真实世界功能编程前言T. Petricek的两个短语:
[...]你可以在C#中使用函数式编程技术,但是在F#中这样做更容易,更自然.[...]功能编程是一种心态.[...]
EDIT 2:
我觉得有必要进一步澄清这个问题.标题中提到的功能严格地与功能编程有关 ; 我不是要求更具功能性的分组方法,从更多的逻辑方式或更一般的方式来看.
这意味着实施将尝试尽可能地遵循由NOOO宣言总结的FP的概念,并在此引用以方便和清楚:
- 类上的函数和类型
- 纯度超过可变性
- 继承的构成
- 方法调度的高阶函数
- 空值选项
问题在于如何布局一个按照FP概念编写的C#库,所以(例如)它绝对不是将方法放在数据结构中的选项; 因为这是一个基于面向对象的范式.
编辑-3:
此外,如果问题得到回应(以及各种评论),我不想给人一种错误的印象,即曾经说过一种编程范式优于另一种编程范式.和以前一样,我会在其专着F#3.0(第20章 - 设计F#图书馆 - 第565页)中提到FP的权威,Don Syme:
[...]一种常见的误解是功能和OO编程方法竞争; 事实上,它们基本上是正交的.[...]
\n\n\n注:如果您想要更简短、更切题的答案,请参阅我的另一个答案。我知道这里可能看起来很漫无目的,并且一直在谈论您的问题,但也许它会给您一些想法。
\n
Animal如果不知道和之间的确切关系,很难回答你的问题Skeleton。我将在我的答案的后半部分提出关于这种关系的建议,但在此之前,我将简单地同意我在你的帖子中看到的内容。
首先,我将尝试从您的代码中推断出一些内容:
\n\nstatic class AnimalTopology\n{\n // Note: I made this function `static`... or did you omit the keyword on purpose?\n static IEnumerable<Animal> ListVertebrated(Func<Skeleton, bool> selector)\n {\n \xe2\x80\xa6\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n如果您根据功能原理设计了该功能,那么它应该没有副作用。也就是说,它的输出仅依赖于它的参数。(在半面向对象的设置中,也许在AnimalTopology; 的其他静态成员上,但由于您没有显示任何内容,让我们忽略这种可能性。)
如果该函数确实没有副作用(并且不访问 的静态成员AnimalTopology),则该函数的类型签名表明可以Animal从 a派生 an Skeleton,因为它接受作用于Skeletons 的内容并返回Animals 。
如果这也是真的,那么为了能够给出答案,让我假设以下内容:
\n\nclass Skeleton\n{\n \xe2\x80\xa6\n public Animal Animal { get { \xe2\x80\xa6 } } // Skeletons have animals!? We\'ll get to that.\n}\nRun Code Online (Sandbox Code Playgroud)现在很明显你的函数是不可能实现的,因为它可以Animal从Skeletons 派生 s,但它根本不接收任何东西Skeleton;它只接收作用于 a 的谓词函数Skeleton。(您可以通过添加第二个类型的参数来解决此问题Func<IEnumerable<Skeleton>> getSkeletons,但是......)
在我看来,像下面这样的东西会更有意义:
\n\nstatic IEnumerable<Animal> GetVertebrates(this IEnumerable<Skeleton> skeletons,\n Func<Skeleton, bool> isVertebrate)\n{\n return skeletons\n .Where(isVertebrate)\n .Select(s => s.Animal);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n现在,人们可能想知道为什么你要根据骨骼来猜测动物?“是脊椎动物”这个属性难道不是bool动物(或骨骼)的固有属性吗?真的有几种方法可以决定吗?
我建议如下:
\n\nclass Animal\n{\n Skeleton Skeleton { get; } // not only vertebrates have skeletons! \n}\n\nclass Vertebrate : Animal { \xe2\x80\xa6 } // vertebrates are a kind of animal \n\nstatic class AnimalsExtensions\n{\n static IEnumerable<Vertebrate> ThatAreVertebrates(this IEnumerable<Animal> animals)\n {\n return animals.OfType<Vertebrate>();\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n请注意上面扩展方法的使用。这是如何使用它的示例:
\n\nList<Animal> animals = \xe2\x80\xa6;\nIEnumerable<Vertebrate> vertebrates = animals.ThatAreVertebrates();\nRun Code Online (Sandbox Code Playgroud)\n\n现在假设您的扩展方法做了更复杂的工作。在这种情况下,将其放入自己指定的“算法类型”中可能是个好主意:
\n\ninterface IVertebrateSelectionAlgorithm\n{\n IEnumerable<Vertebrate> GetVertebrates(IEnumerable<Animal> animals);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这样做的优点是可以通过类构造函数来设置/参数化;您可以将算法拆分为多个方法,这些方法都位于同一类中(但除了 之外private。GetVertebrates)
当然,您可以使用函数闭包执行相同类型的参数化,但根据我的经验,这在 C# 设置中很快就会变得混乱。在这里,类是将一组函数组合在一起作为一个逻辑实体的好方法。
\n| 归档时间: |
|
| 查看次数: |
735 次 |
| 最近记录: |