14.2.1 F# 中的颜色计算

来源:互联网 发布:centos6.7网络配置 编辑:程序博客网 时间:2024/05/29 13:55

14.2.1  F# 中的颜色计算

 

    若要实现图形效果,比如模糊或灰阶化,我们需要执行颜色的计算。我们可以使用 System.Drawing 中标准的 Color 类型,但是,我们可能需要把它分开成红色、绿色和蓝色的组件处理,这并不总是很方便。

    有一种更自然的方式,在 F# 和 C# 中执行这些计算。我们可以使用运算符重载,实现我们自己的颜色类型。当我们在后面模糊图像时,可以简单地把颜色加到一起,并把结果颜色以几个像素为单位分开。你可能已经知道,如何在 C# 中实现,可以在本书的网站下载源代码中找到这个实现。清单 14.9 展示了 F# 版本。

 

Listing 14.9 Implementing color type with operators (F#)

 

[<Struct>] 
type SimpleColor(r:int, g:int, b:int) = 
  member x.R = r 
  member x.G = g 
  member x.B = b 
  member x.ClipColor() = 
    let check(c) = min 255 (max 0 c) 
    SimpleColor(check(r), check(g), check(b)) 
  static member (+) (c1:SimpleColor, c2:SimpleColor) = 
    SimpleColor(c1.R + c2.R, c1.G + c2.G, c1.B + c2.B) 
  static member (*) (c1:SimpleColor, n) = 
    SimpleColor(c1.R * n, c1.G * n, c1.B * n) 
  static member DivideByInt (c1:SimpleColor, n) = 
    SimpleColor(c1.R / n, c1.G / n, c1.B / n) 
  static member Zero = SimpleColor(0, 0, 0)

 

    类型使用 .NET 的属性 Struct 注释,这是一个专门属性,用于指示 F# 编译器把类型编译为值类型,与之对应的 C# 关键字是 struct。在此示例中,使用值类型是重要的,因为我们将创建一个这些值的数组,并在堆上为每个像素分配一个新的对象,将是非常低效的。

    类型提供一个构造函数,它取颜色的红色、绿色和蓝色组件,并通过成员公开它们。注意,声明值类型时,我们要显式提供所有参数的类型。这些参数指定类型的字段,定义值类型的结构,因此,看到它们是很有用的。然后,类型提供一个成员,如果他们是小于 0 或超过 255,组件的值会被剪辑。这可用于创建有效的颜色,使所有组件的值在 0-255 的范围内。下一步,类型提供了运算符的重载,为组件按位(component-wise ) 颜色加法,和组件乘以一个整数。

    就像在 C# 中,重载的运算符实现为该类型的静态成员。我们已经看到另一种方式在 F# 实现运算符(在第 6 章),我们用 let 绑定声明,像函数一样。重载的运算符是更适合的,如果运算符是这个类型的一个固有部分。管道运算符(|&gt;)逻辑上不属于任何类型,而我们的运算符是专门给 SimpleColor 的。

    有些 F# 的库函数可以处理任何提供基本运算符和成员的类型。这也是为什么我们提供 Zero 成员的原因,它返回黑颜色。当一个类型有加号运算符和 Zero 成员时,对于任何 clr,clr = clr + T.Zero 应该是 true。我们可以看到,对于我们的类型,它是 true。DivideByInt 成员是某些 F# 库函数所预期的另一个名字,马上就会看到。它执行颜色值的整数除法。我们可以提供这种功能,比如 / 运算符,但是,/ 运算符有相同类型的操作数,更常见的,所以,如果我们想要实现除法,取两种颜色作为其参数值,我们将会使用它。

    这个类型的另一个重要方面是不可变性。任何操作都不能修改已有的值,而是返回新的颜色(实例成员 ClipColor)。即使你不以函数风格编程,当你写自己的值类型时,这仍是好的做法。在各种微妙的方式中,可变的值类型可能令人头痛。

    现在,我们已经有一个类型,表示颜色,让我们看看如何表示图形滤镜,以及如何运行它们。我们还没有并行化这种操作,在尝试并行化之前,写可以正常工作的代码,以顺序运行,通常是值得的,同时铭记并行化。

原创粉丝点击