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#
这个函数首先初始化测量时间的 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>。我们首先将返回的数组转换成位图,显示位图,并显示应用这个效果所花的时间。
我们目前只关注这个效果的性能,但在位图和数组之间的并行化转换也是可能的。如果你有兴趣,我们留给你作为一个练习,但此刻,我们将继续并行化这个效果。
在 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>。我们首先将返回的数组转换成位图,显示位图,并显示应用这个效果所花的时间。
我们目前只关注这个效果的性能,但在位图和数组之间的并行化转换也是可能的。如果你有兴趣,我们留给你作为一个练习,但此刻,我们将继续并行化这个效果。
相关文章推荐
- virtual pc 2007 与 vmware比较之总结!
- VMware,virtual PC, Bochs三种虚拟机的比较
- VMWare与Virtual PC比较
- VMWare和Virtual PC虚拟机的比较
- Integer和int使用==比较的总结
- 自定义php开发环境搭建以及两种方式的比较总结
- 在Virtual PC 2007中安装ubuntu
- Windows与Vmware下的Linux文件共享方式总结
- 20155207 2006-2007-2 《Java程序设计》第5周学习总结
- GDCPC 2007 应该说还是比较高兴的...(2007-04-23 00:15)
- ESRI2007应用开发大赛总结
- 各种进程创建方式比较总结(MacOS, Win32
- 蓝牙与802.11的简要总结与比较
- xml总结(六)dom4j,xml处理技术比较
- C/C++中static关键字作用总结 && 指针与引用的比较
- Windows与Vmware下的Linux文件共享方式总结
- 总结js中数据类型的bool值及其比较
- KVM&Xen&Vmware的比较
- vmware经验总结--3.VMWare esx,esxi使用入门
- java集合遍历的几种方式总结及比较