您的位置:首页 > 其它

virtual pc 2007 与 vmware比较之总结

2009-08-28 16:15 274 查看
14.2.4 创建和运行效果
 
    在 14.2.2 节中,我们实现了几个颜色滤镜,但是,我们的应用程序包含更一般的图形效果的列表。看来我们还有很多工作要做,但实际上,从简单的颜色滤镜创建图形的效果所需要的一切,我们已经都有了。在下一节,每一个有意义的事情都会开始,在那儿,你将学习如何从颜色滤镜创建一个效果。
 
在 C# 中从颜色滤镜创建效果
 
    要基于颜色滤镜创建通常的图形的效果,可以将滤镜应用于图像的所有像素。我们已经实现了它的序列形式,作为 RunFilter 方法。让我们开始写一个简单的函数,使用 RunFilter 来创建一个效果,按顺序的方式运行给定的滤镜,很快,我们会讨论并行版本。在清单 14.14 中你可以看到,我们使用 lambda 函数构造一个效果,并从这个方法中返回一个委托。
 
Listing 14.14 Creating graphical effect from a color filter (C#)
 
Func<SimpleColor[,],SimpleColor[,]> MakeEffect
    (Func<SimpleColor, SimpleColor> filter) {
  return arr => Filters.RunFilter(arr, filter);
}
 
    这个方法有只有一个参数,是一个颜色滤镜,我们用来转换成效果的。效果应将此滤镜应用于图像的每个像素,但是,当我们还没有图像时,该怎样做呢?答案是,在后面,从表示影响的函数的一个参数中,我们会得到这个图像,所以,这个方法体通过 lambda 函数返回效果。
    这个 lambda 函数只有一个参数:需要处理的图像。一次我们有了这个信息,就可以调用 RunFilter 方法。如果你还记得,在第 8 章中,我们讨论过有关闭包(closure),你知道,方法的参数 filter 将由闭包捕获,它与返回的函数相关联。
 
    务必要注意,返回的类型与保存在 EffectInfo 中的类型是完全相同的,所以,当我们为工具栏构建效果的下拉列表时,可以立即使用它。当我们随后讨论图像的滤镜并行化时,会实现这个处理方法的并行版本 RunFilterParallel,我们也需要为 MakeEffect 方法添加并行版本,但这只是另一个三行的方法,只适应这个方法签名。
    下面是如何从两个现有颜色滤镜创建效果,并将它们添加到 listFilter 控件的示例。当我们完成 MakeEffect 的并行版本时,也能够添加的并行版本:
 
var effects =
  new List<EffectInfo> {
    new EffectInfo {
      Name = "Grayscale (sequential)",
      Effect = MakeEffect(Filters.Grayscale) },
    new EffectInfo {
      Name = "Lighten (sequential)",
      Effect = MakeEffect(Filters.Lighten) }
  };
listFilters.ComboBox.DataSource = effects;
listFilters.ComboBox.DisplayMember = "Name";
 
    我们将使用 C# 3.0 集合初始值设定项(collection initializer)来创建 List<EffectInfo>,包含我们已经创建的两个颜色滤镜的有关信息。当我们调用 MakeEffect 时,给它一个来自 Filters 类的方法组作为参数。这个方法组自动由 C# 编译器转换为 Func 委托。最后两行把这个列表设置成下拉控件的数据源,并使用 DisplayMember 属性来指定效果名应该显示的文本。
    这个应用程序的 F# 版本的对应代码是很有趣的,虽然我们还没有看到完整的代码,不过会在这一部分讨论。
 
在 F# 中使用偏函数(PARTIAL FUNCTION APPLICATION)应用
 
    这一问题的 F# 解决将变得相当容易。我们刚才已经实现的方法,在 C# 中只改变了我们提供给 RunFilter 方法的参数值的方式。它称为 MakeEffect,以更好地反映它除了名字以外,还会返回什么,但这并无太大的不同。要更好地理解这个,让我们看看类型。这里是 MakeEffect 方法的类型签名,使用 F# 符号:
 
(SimpleColor-> SimpleColor)-> (SimpleColor [,]-> SimpleColor[,])
 
    让我们回顾一下,我们已经实现的 F# 的 runFilter 函数,来替代在 C# 中的 RunFilter 方法。我们已经看到,它是处理二维数组的泛型函数。如果我们用实际类型,它将用来表示像素,来替换类型参数,我们就会得到以下的签名:
 
(SimpleColor-> SimpleColor)-> SimpleColor [,]-> SimpleColor [,]
 
    这两个签名之间的唯一区别,是第一个函数结果返回的一个函数,而第二个函数取两个参数值,并返回已处理的数组。我们在第 5 章中学习过,F# 的编译器会把这两个函数好像是一样的看待。这意味着,如果我们调用 F# 的函数 runFilter,使用偏应用,并只指定第一个参数值(颜色滤镜函数),我们就能获得一个函数,把效果表示成结果:
 
> let effect = runFilter ColorFilters.Grayscale;;
val effect : (SimpleColor[,] -> SimpleColor[,])
 
    如你所见,由于有了偏函数应用,我们不需要任何 F# 函数,相当于 MakeEffect 方法,我们自由地从滤镜转换到效果。我们可以写 C# 的 RunFilter 方法,在某种程度上,其行为与 MakeEffect 相同,但这不是写 C# 代码的自然方式,所以,我们选择添加一个简单的方法,作为简单的界面。像这个描述所暗示的,这种形式的转换并不以任何方式,不同于界面的设计模式。
    现在,让我们返回到用户界面,再看看应用按钮的事件处理程序还必须做什么。
 
执行图形效果
 
    当我们应用这个效果时,需要测量它所花的时间。我们应该记住运行效果之前的时间,然后,运行这个影响,从当前时间减去原始时间。然而,它混淆了时间方面与调用的方面。如果我们想要测量在不同的地方的时间,需要复制和粘贴代码,这并不是一个好的做法。函数式编程使我们有更好的方法,来解决这个问题。
    我们可以用高阶函数实现时间测量,它取另一个函数作为参数值,并测量运行所花的时间。返回值是一个元组,包含该函数的结果和花费的时间,以毫秒计。清单 14.15 显示了 F# 和 C# 的实现。
 
Listing 14.15 Measuring the time in C# and F#
 
C# F#
using System.Diagnostics;
Tuple<T, long> MeasureTime<T>
    (Func<T> f) {
  var st = Stopwatch.StartNew();
  var res = f();
  var t = st.ElapsedMilliseconds;
  return Tuple.Create(res, t);
}
open System.Diagnostics
let measureTime(f) =
  let st = Stopwatch.StartNew()
  let res = f()
  let t = st.ElapsedMilliseconds
  (res, t)
 
    这个函数首先初始化测量时间的 Stopwatch 类,然后,运行指定的函数。我们不想扔掉结果,因此,将它存储在本地,计算花费的时间。因为我们需要从函数返回多个值,因此,使用一个元组值。元组的第一个元素是传递进去的函数的结果,它可以是任何类型,取决于该函数。第二个元素将包含所花费的时间,以毫秒计。
    清单 14.16 使用了这个新方法,在运行按钮的 Click 事件的事件处理程序中。
 
Listing 14.16 Applying the selected effect to a bitmap (C#)
 
var info = (EffectInfo)listFilters.SelectedItem;
var effect = info.Effect;
var arr = loadedBitmap.ToArray2D();
var res = MeasureTime(() => filter(arr));
pictProcessed.Image = res.Item1.ToBitmap();
lblTime.Text = string.Format("Time: {0} ms", res.Item2);
 
    在下拉列表中可用的效果,保存为 EffectInfo 的实例,因此,我们首先访问列表中的选择项。一旦我们有了这个效果,就可以执行位图图像的处理。我们首先将位图转换为二维数组,然后,再应用这个滤镜。这个操作被打包到对 MeasureTime 方法的调用,所以,res 的类型是 Tuple<SimpleColor[,], long>。我们首先将返回的数组转换成位图,显示位图,并显示应用这个效果所花的时间。
    我们目前只关注这个效果的性能,但在位图和数组之间的并行化转换也是可能的。如果你有兴趣,我们留给你作为一个练习,但此刻,我们将继续并行化这个效果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: