15.3.4 在 C# 中实现提升与映射

728 篇文章 1 订阅
48 篇文章 0 订阅
 

15.3.4 在 C# 中实现提升与映射

 

    提升函数和映射操作是构建行为的核心,所以,在该项目的 C# 版本中也需要它们。在前面探讨 F# 版本之后,对于这些函数应该做什么,你会有一些概念,所以,我们不会详细讨论全部内容。C# 版本与 F# 代码的差异很有趣。

    每当我们看到 F# 中的 map 函数,在 C# 中使用 Select。这是 LINQ 中使用的标准术语,所以,我们会保持一致,为行为实现 Select 扩展方法。我们刚才提到,在 C# 中Select 方法与提升的方法有差异。了解差异的最佳方式,是看一下 Select 和最简单的提升方法的函数签名:

 

// Apply the function 'f' to values of 'behavior'
Behavior<R> Select<T, R>(Behavior<T> behavior, Func<T, R> f);

// Returns a function that applies 'f' to the given behavior
Func<Behavior<T>, Behavior<R>> Lift<T, R>(Func<T, R> f);

 

    在 C# 中,我们可以创建重载方法,这样,不同类型的函数提升方法都可以称为 Lift。前面代码段的版本取一个参数(函数),并返回一个函数(Func 委托)。Select 方法取在应用的这个函数和行为作为参数,所以,它可以使用这个函数立即构建新的行为。这些函数的实现是类似的,所以,我们仍然看它们相关的,但是,我们不能简单地使用相同的代码,实现它们。(使用其他函数来实现这些函数是可能的。如果想练习你的函数式思维技巧,可以尝试这样做。)清单 15.11 显示 Select 的实现和 Lift 的两个重载。

 

Listing 15.11 Lifting methods and Select (C#)

 

public static Behavior<R> Select<T, R>
    (this Behavior<T> behavior, Func<T, R> f) {
  return Create(ctx => f(behavior.BehaviorFunc(ctx)));
}

public static Func<Behavior<T>, Behavior<R>>
    Lift<T, R> (Func<T, R> f) {
  return behavior => Create(ctx => f(behavior.BehaviorFunc(ctx)));
}


public static Func<Behavior<T1>, Behavior<T2>, Behavior<R>>
    Lift<T1, T2, R>(Func<T1, T2, R> f) {
  return (b1, b2) => Create(ctx =>
    f(b1.BehaviorFunc(ctx), b2.BehaviorFunc(ctx)));
}

 

    我们已经将所有扩展方法都添加到静态、非泛型的 Behavior 类,包含 internal Create 方法。Select 的实现是 F# 版本的直接转换,它构造一个行为,并给它一个函数,使用原来的行为(behavior)和提供的函数,计算指定时间的值。

    第二个方法更有趣,因为它返回一个函数。我们用 lambda 函数来实现,取这个行为作为参数,做的事与前面的方法相同。最后,我们实施另一个重载,有类似,但处理两个参数的函数。使用这些方法,可以构造的相同的行为,像 F# 中那样做的。创建一种行为,表示时间的平方的最好的方法,是使用 Select 扩展方法。为了添加两种基元行为,我们将创建提升的加法函数,然后使用它:

 

var squared = Time.Current.Select(t => t * t);

var plusB = Behavior.Lift((float a, float b) => a + b);
var added = plusB(Time.Current, Time.Wiggle);

 

    第一个示例应该是相当简单的,它使用 Select 方法指定一个函数,将用于计算行为的平方值。第二个示例首先声明值 plusB,这是一个函数,可以加两个浮点类型的行为,此函数的总体类型是很长的:

 

Func<Behavior<float>, Behavior<float>, Behavior<float>>

 

    幸运的是,我们可以使用隐式类型化的局部变量,避免源代码凌乱。一旦我们有了提升的 + 运算符,就可以使用它把两个行为加到一起。这里,我们把表示当前时间和 wiggle 基元的行为加起来,结果是另一个行为 (更具体地说,是Behavior<float>)。

    对于动画框架灭族,行为至关重要,而且是最难的方面。下一步是决定如何绘制形状。我们还没有让它们动起来,我们将绘制单个帧。

 

行为与 LINQ

 

    在清单 15.11 中的 Select 方法的签名,与LINQ 查询中的Select 方法,具有相同的结构。这不是偶然的,它意味着,可以使用 C# 查询表达式来创建行为。这里是创建平方行为的另一种方式:

 

var squared = from t in Time.Current select t * t;

 

    它的意思与前面的版本相同:编译器将查询表达式转换成相同的代码。查询的类型是有趣的,因为值(Time.Wiggle)源效地包含了可能的无限集的值。"查询"只在我们需要创建特定时间,新的行为值时,才会计算。我们不会在这里,更说细地讨论行为的 LINQ 查询,不过还实现了 SelectMany 查询运算符,能够这样组合行为:

 

var added = from a in Time.Current
                  from b in Time.Wiggle
                  select a + b;

 

    这绝对是一个有趣的替代使用显式提升。在 F# 中,我们也可以实现计算表达式生成器。可以本书的网站上找到这些有趣扩展的实现。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值