首页 >> 大全

c#函数式编程 Functional Programming in C# [4]

2024-01-01 大全 33 作者:考证青年

1.4 高阶函数

现在你已经了解了什么是FP,我们也回顾了语言的功能特性,现在是时候开始探索一些具体的功能技术了。我们将从函数作为第一公民的最重要的好处开始:它使你有能力定义高阶函数(HOFs)。

HOFs是将其他函数作为输入或将一个函数作为输出返回的函数,或者两者都是。我假定你已经在某种程度上使用过HOFs,比如用LINQ。在本书中,我们会经常使用HOFs,所以本节应作为复习,并可能介绍一些你可能不太熟悉的HOFs用例。HOFs很有趣,本节中的大多数例子都可以在REPL中运行。请确保你在这一过程中尝试一些自己的变化。

1.4.1 依赖于其他函数的函数

一些 HOF 将其他功能作为参数并调用它们以完成其工作,有点像一家公司可能会将其某些工作分包给另一家公司。您已经在本章前面看到了一些此类 HOF 的示例:Sort(List 上的实例方法)和 Where( 上的扩展方法)。

List.Sort,当与委托一起调用时,是一个说:"好吧,我自己排序,只要你告诉我如何比较我包含的任何两个元素。"Sort做排序的工作,但调用者可以决定使用什么逻辑进行比较。

同样地,Where做的是过滤的工作,调用者决定用什么逻辑来判断一个元素是否应该被包含。你可以用图形来表示Where的类型,如图1.4所示。

让我们看一下 Where 的理想化实现

清单1.10 Where:一个典型的HOF,反复应用给定的谓词。

publicstatic IEnumerable<T> Where<T> (this IEnumerable<T> ts, Func<T, bool> predicate)
{foreach (T t in ts)if (predicate(t))yield return t;
}

Where方法负责排序逻辑,调用者提供,这是应该被过滤的标准。

正如你所看到的,HOF可以在逻辑不容易分离的情况下帮助实现关注点的分离。Where和Sort是迭代应用的例子–HOF将对集合中的每个元素重复应用给定的函数。

一个非常粗略的方法是,你传递的参数是一个函数,其代码最终将在HOF中的循环体中执行–这是你只传递静态数据所做不到的。一般的方案显示在图1.5中

c函数式编程_c#函数式编程pdf网盘下载_

可选执行是HOF的另一个好办法。 当你想只在特定的条件下调用一个给定的函数时,这很有用,如图1.6所示。

例如,设想有一个方法可以从缓存中查找一个元素。可以提供一个委托,并在缓存丢失的情况下被调用。

清单 1.11 可选调用给定函数的 HOF

class Cache < T > where T: class {public T Get(Guid id) => //...public T Get(Guid id, Func < T > onMiss) => Get(id) ?? onMiss();
}

中的逻辑可能涉及到一个昂贵的操作,如数据库调用,你不会希望这个操作被不必要地执行。

前面的例子说明了将一个函数作为输入的HOF(通常被称为回调或延续),并使用它来执行一个任务或计算一个值。这也许是HOF最常见的模式,它有时被称为控制倒置:HOF的调用者通过提供一个函数来决定做什么,而被调用者通过调用给定的函数来决定何时做。

让我们看看 HOF 派上用场的其他一些场景。

1.4.2 适配器功能

有些HOF根本不应用给定的函数,而是返回一个新的函数,与作为参数的函数有某种联系。例如,假设你有一个执行整数除法的函数:

Func<int, int, int> divide = (x, y) => x / y;
divide(10, 2) // => 5

你想改变参数的顺序,使除数排在前面。 这可以看作是一个更普遍的问题的一个特殊案例:改变参数的顺序。

你可以写一个通用的HOF,通过调换参数的顺序来修改任何二进制函数。

_c#函数式编程pdf网盘下载_c函数式编程

static Func<T2, T1, R> SwapArgs<T1, T2, R>(this Func<T1, T2, R> f)=> (t2, t1) => f(t1, t2);

从技术上讲,更正确的说法是 返回一个新的函数,该函数以相反的顺序调用给定的函数的参数。但从直观的角度看,我发现认为我得到的是原始函数的一个修改版本更容易。

现在你可以通过应用来修改原来的除法函数:

var divideBy = divide.SwapArgs();
divideBy(2, 10) // => 5

在玩这种HOF的时候,我想到了一个有趣的想法,那就是函数并不是固定不变的:如果你不喜欢一个函数的接口,你可以通过另一个函数来调用它,这个函数提供了一个更适合你需要的接口。这就是为什么我把这些函数称为适配器。

1.4.3 创建其他函数的函数

有时你写的函数的主要目的是创建其他函数–你可以把它们看作是函数工厂。 下面的例子使用一个来过滤一个数字序列,只保留能被2整除的数字:

var range = Enumerable.Range(1, 20);
range.Where(i => i % 2 == 0)
// => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

如果你想要更普遍的东西,比如能够过滤被任何数字n整除的数字,会怎么样?你可以定义一个函数,接收n并产生一个合适的谓词,来评估任何给定的数字是否能被n整除:

Func<int, bool> isMod(int n) => i => i % n == 0;

我们以前没有看过这样的HOF:它接收一些静态数据并返回一个函数。让我们看看你如何使用它:

using static System.Linq.Enumerable;
Range(1, 20).Where(isMod(2)) // => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Range(1, 20).Where(isMod(3)) // => [3, 6, 9, 12, 15, 18]

请注意,你不仅在通用性方面得到了提高,而且在可读性方面也得到了提高 在这个例子中,你使用isMod HOF来产生一个函数,然后你把它作为输入送给另一个HOF,Where,如图1.7所示

你会在书中看到更多的HOF的用法。最终你会把它们看成是常规函数,而忘记了它们是高阶函数。现在让我们来看看它们如何在更接近日常开发的情况下使用。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了