4.4.2.1 创建随机颜色的画笔

4.4.2.1 创建随机颜色的画笔


我们首先绘制饼形。我们想要使用随机颜色填充饼图指定部分,所以,先要写一个简单的工具函数,创建随机颜色的画笔,用于填充区域,如清单 4.7 所示。


清单 4.7创建随机颜色的画笔 (F#)
let rnd = new Random()
let randomBrush() =
  let r, g, b = rnd.Next(256), rnd.Next(256), rnd.Next(256)
  new SolidBrush(Color.FromArgb(r,g,b))


代码声明了两个顶层值。第一个是 .NET 类 Random 的实例,用于生成随机数;第二是 randomBrush 函数,参数类型为 unit,这是一种 F# 方法,是说它不需要取任何有意义的参数。由于这个参数,我们声明的函数,可以运行多次得到不同的结果。如果我们忽略这个参数,那么,创建的值,只在应用程序启动时,只计算一次。unit 唯一可能的值就是 (),因此,当我们在后面的代码中调用这个函数时,实际上是把 unit 作为参数值,尽管它看起来就像根本不带任何参数值的函数调用。randomBrush 函数使用 rnd 值,生成 SolidBrush 对象,用于填充指定的区域。这个函数有副作用,我们知道,在函数程序中,使用有副作用的函数时,应该小心。

 

隐藏的副作用

 

函数 randomBrush 是函数有副作用的一个示例,即,函数每次调用,可能会返回不同的结果,因为它依赖于一些不断变化的值,而不是函数的参数值。在这个示例中,变化的值是 rnd,它表示随机数发生器,每次调用 Next 方法,其内部状态都会改变。清单 4.7 把 rnd 声明成全局值,尽管它只在函数 randomBrush 内部使用。当然,这是一个提示,我们应把它声明为本地值,尽量减少全局值。重写的代码如下所示:

 

let randomBrush() =

  letrnd = new Random()

  letr, g, b = rnd.Next(256), rnd.Next(256), rnd.Next(256)

  newSolidBrush(Color.FromArgb(r,g,b))

 

但是,这段代码不能工作!问题是,我们每次调用该函数时,都将创建一个新的 Random 对象,内部状态的变化并不保留。在创建时,Random 使用当前时间初始化内部状态,但是,由于绘图程序执行很快,“当前时间”来不及变化,最终我们得到的整个图表都被画成同一个颜色。

不必惊讶,有一种方法写这段代码,不用把 rnd 声明成全局值,却可以保持函数调用表示的可变状态。写这样的代码,需要将在第五章中讨论的两个概念:闭包(closure)和 lambda 函数。我们将在第八章会看到类似的示例,演示了频繁模式(frequent pattern)如何隐藏这种副作用。

 

现在,我们已经知道如何创建填充图表的画笔,可以看一下第一个绘图函数了。


你可能感兴趣的:(实用函数编程,函数编程,可视化,F#)