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

阅读: 评论:0

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

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

1.4 高阶函数

  现在你已经了解了什么是FP,我们也回顾了语言的功能特性,现在是时候开始探索一些具体的功能技术了。我们将从函数作为第一公民的最重要的好处开始:它使你有能力定义高阶函数(HOFs)。
  HOFs是将其他函数作为输入或将一个函数作为输出返回的函数,或者两者都是。我假定你已经在某种程度上使用过HOFs,比如用LINQ。在本书中,我们会经常使用HOFs,所以本节应作为复习,并可能介绍一些你可能不太熟悉的HOFs用例。HOFs很有趣,本节中的大多数例子都可以在REPL中运行。请确保你在这一过程中尝试一些自己的变化。

1.4.1 依赖于其他函数的函数

  一些 HOF 将其他功能作为参数并调用它们以完成其工作,有点像一家公司可能会将其某些工作分包给另一家公司。您已经在本章前面看到了一些此类 HOF 的示例:Sort(List 上的实例方法)和 Where(IEnumerable 上的扩展方法)。
  List.Sort,当与Comparison委托一起调用时,是一个说:"好吧,我自己排序,只要你告诉我如何比较我包含的任何两个元素。"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方法负责排序逻辑,调用者提供predicate,这是IEnumerable应该被过滤的标准。
  正如你所看到的,HOF可以在逻辑不容易分离的情况下帮助实现关注点的分离。Where和Sort是迭代应用的例子–HOF将对集合中的每个元素重复应用给定的函数。
  一个非常粗略的方法是,你传递的参数是一个函数,其代码最终将在HOF中的循环体中执行–这是你只传递静态数据所做不到的。一般的方案显示在图1.5中

  可选执行是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();
}

  onMiss中的逻辑可能涉及到一个昂贵的操作,如数据库调用,你不会希望这个操作被不必要地执行。
  前面的例子说明了将一个函数作为输入的HOF(通常被称为回调或延续),并使用它来执行一个任务或计算一个值。这也许是HOF最常见的模式,它有时被称为控制倒置:HOF的调用者通过提供一个函数来决定做什么,而被调用者通过调用给定的函数来决定何时做。
  让我们看看 HOF 派上用场的其他一些场景。

1.4.2 适配器功能

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

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

  你想改变参数的顺序,使除数排在前面。 这可以看作是一个更普遍的问题的一个特殊案例:改变参数的顺序。
  你可以写一个通用的HOF,通过调换参数的顺序来修改任何二进制函数。

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

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

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

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

1.4.3 创建其他函数的函数

  有时你写的函数的主要目的是创建其他函数–你可以把它们看作是函数工厂。 下面的例子使用一个lambda来过滤一个数字序列,只保留能被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的用法。最终你会把它们看成是常规函数,而忘记了它们是高阶函数。现在让我们来看看它们如何在更接近日常开发的情况下使用。

本文发布于:2024-01-31 16:08:36,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170668851429754.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:函数   Functional   Programming
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23