15.3.3 将函数转换成“行为函数”

15.3.3 将函数转换成“行为函数”

Behavior.map 函数取两个参数,我们在清单 15.8 中指定了。偏函数应用可以用一个参数值来调用这个函数。以这种方式使用函数,会给我们一个有趣的见解。在清单 15.9 中,我们只指定第一个参数值 (函数);我们将使用 abs 函数,返回一个整数的绝对值。

Listing 15.9 Using Behavior.map with partial application (F# Interactive)

> abs;;
val it : (int -> int)

> let absB = Behavior.map abs;;
val absB : (Behavior<int> -> Behavior<int>)

第一行显示了 abs 函数类型,第二行显示,如果我们调用 Behavior.map 函数,用 abs 作为第一个参数值,也是唯一的参数值时,会发生什么。结果的类型是一个函数,取 Behavior<int>,返回 Behavior<int>。这意味着,我们用 Behavior.map 来创建一个函数,计算行为值的绝对值。我们可以使用这种技巧,把任何取一个参数函数转换成另一个函数,对这个行为做同样的事。

运算符和函数的提升(lifting)

刚才我们看到构造是在函数式编程中一个著名的概念,通常称为提升(lifting)。在某种程度上,我们可以甚至称之为函数式的设计模式。Haskell wiki [HaskellWiki,2009] 对提升有一个定义,可以把处理值的函数转换成另外的函数,做同样的工作,以不同的设置。提升采取的是 C# 2.0 语言的功能,所以,我们可以使用熟悉的代码来演示。如果我们想创建一个基元值,比如 int,它可以有 null 值,可以使用 C# 2.0 可空类型:

int? num1 = 14;
int? num2 = null;

到目前为止,没有什么不同凡响的。我们声明了两个可空的 int 值,一个包含一个真正的整数值,另一个没有值。你不可能知道可以像这样写:

int? sum1 = num1 + num2;
int? sum2 = num1 + num1;

第一个计算的结果是 null,因为至少有一个参数值是 null。第二个表达式的结果是 28,因为,运算符 + 的两个参数值都有值。在此示例中,C# 编译器取 + 运算符,处理整数,并创建提升的 + 运算符,能够处理可空的类型。这个操作类似于我们行为所要做的。

Behavior.map 为有一个参数的函数实现了提升,但是,我们想要为其他函数实现相同的功能。清单 15.10 显示了几个辅助函数,允许有最多三个参数提升的函数。

Listing 15.10 Lifting functions of multiple arguments (F#)

> let lift1 f behavior =
map f behavior
let lift2 f (BehaviorFunc bf1) (BehaviorFunc bf2) =
sample(fun t -> f (bf1(t)) (bf2(t)))
let lift3 f (BehaviorFunc bf1) (BehaviorFunc bf2) (BehaviorFunc bf3) =
sample(fun t -> f (bf1(t)) (bf2(t)) (bf3(t)))
;;
val lift1 : ('a -> 'b) -> B<'a> -> B<'b>
val lift2 : ('a -> 'b -> 'c) -> B<'a> -> B<'b> -> B<'c>
val lift3 : ('a -> 'b -> 'c -> 'd) -> B<'a> -> B<'b> -> B<'c> -> B<'d>

我们将把所有的函数放到 Behavior 模块中,但是,清单没有重复模块的声明。我们的示例首先显示了如何分别实现提升函数,显示它们的签名。注意,我们在打印的类型签名中,把 Behavior 简称为 B。

实现的有一个参数的提升函数相当简单,因为它做的事与 Behavior.map 相同。这是可能的,唯一是因为有了偏函数应用,因此,C# 的实现将是不同的。lift2 和 lift3 的实现是类似于我们前面看到的 map 函数,相同的模式可清楚地应用于三个以上参数的函数。

在这里,我们可以实现行为的通用计算,而无需使用低级的 sample 基元。任何你能想到的计算可以使用 time 基元和一个提升函数实现。这是我们早前的两种行为相加的问题的解决:

> let added = Behavior.lift2 (+) wiggle time;;
val added : Behavior<float32>

这个示例使用 lift2 函数,传递 + 运算符和两个基元行为。如果我们读取返回的行为值,它将得到两个用作参数的行为的值,并将它们加到一起。我们可以用像之前的相同方式可视化这种行为。图 15.3 显示了这种行为和"平方"行为的版本,稍做修改以避免它太快地关闭屏幕抓图的顶部。


图 15.3 图形显示两种复杂行为最初 10 秒的值

你可能会想知道,是否这就是我们在语法方面能做的最好的,毕竟,它仍然有点笨拙。事实上,稍后我们会看到,只需要写成 wiggle + time,但是,首先让我们在 C# 中实现 Behavior.map 和提升函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值