wodehuajianrui

博客园 首页 新随笔 联系 订阅 管理
  34 Posts :: 5 Stories :: 132 Comments :: 2 Trackbacks

2009年9月12日 #

其实这是一道面试题,要求将两个有序的长度分别为L1、L2的数组,融合成为一个有序的长度为L1+L2的数组,数组类型不限,需要提供标准接口。
我一想,这个东西简单,用范型呗,在.net下无非就是写个方法,方法原型为:
T[] Merge(T[] left, T[]right, IComparer<T> comparer);
实现也简单,里面的参数check也简单,同样的,在JAVA这种支持范型的语言下,基本上也是类似的东西。
不过问题是,人家让用C写,题目上说的是参照函数库qsort(快速排序)写。我只能说,C语言里的qsort我不但没用过,而且没见过……
好吧,C就C,虽然没见过,至少咱不傻。范型不是没有么?没关系,咱用void*,接口不是也没有么?没关系,咱用函数指针。
老早就把函数指针原型定义好:typedef int (func)(void*,void*);
然后我第一版的函数接口为:void* msort(void* left, int lenght1, void* right, int length2, func* comparer, int typeLength);
具体实现简单,看过归并的同学都知道,我最后再写。
人家一看,你把结果作为返回类型了,不好,C里面基本就是return 个int,告诉你是不是success了。于是我改,第二版如下:
int msort(void* left, int length1, void* right, int length2, void* result, func* comparer, int typeLength);
人家看了说,你这些指针,都能被改变,不安全,完蛋,好吧,加上const。于是第三版就成了这样:
int msort(const void* left, int length1, const void* right, int length2, const func* f, const void* result, int typeLength);
用JAVA这C#这种语言用久了,早不去想什么是指针了。人们有人说这种语言是快餐式的,也有人说是傻瓜式的,总之它让你不动脑子。
如果入口参数是一个T的数组,什么都免了,我不需要它的长度,因为T[].Length能告诉我,我也不用关心const的问题,因为你随便加它的index,它首地址永远不会变。
感慨发完之后,附上今天我整理的程序代码,我没在C下写过工程,可能会有很多不规范,甚至可能有问题,希望高手指出。
Code
posted @ 2009-09-12 11:10 花间蕊 阅读(65) 评论(0)  编辑

2009年8月18日 #

只有代码:

Code
虚函数表那个其实是扯淡的,RTTI用于类似反射的机制只能做到这么多,ISO不让把一个成员方法作为函数指针使用,就没什么好办法了。
posted @ 2009-08-18 17:19 花间蕊 阅读(41) 评论(0)  编辑

题目来源于ACM竞赛。
要求,输入任意一个正整数,计算得到2^N%N,需要考虑溢出,并且有时间限制。
按照正常的循环求指数的运算,基本上在N比较大的时候一定会超时,因此为了实现这个要求只能寻求时间复杂度小于O(N)的算法。
相应的算法及测试代码如下:
 1    class Program
 2    {
 3        static void Main(string[] args)
 4        {
 5            Console.WriteLine("请输入一个正整数:");
 6            bool flag = false;
 7            int test = 0;
 8            while (!flag)
 9            {
10                string s = Console.ReadLine();
11                flag = int.TryParse(s, out test);
12                flag = flag && (test > 0);
13                if (!flag)
14                    Console.WriteLine("格式不正确,请重新输入:");
15                else
16                    Console.WriteLine("您输入的正整数为:{0}", test);
17            }

18
19            Stopwatch sw = new Stopwatch();
20            sw.Start();
21            int fast = computeFast(test);
22            sw.Stop();
23            Console.WriteLine("O(logN)计算结果为{0}", fast);
24            Console.WriteLine("O(logN)算法耗时{0}ms", sw.ElapsedMilliseconds);
25            sw.Reset();
26            sw.Start();
27            int slow = computeSlow(test);
28            sw.Stop();
29            Console.WriteLine("O(N)计算结果为{0}", slow);
30            Console.WriteLine("O(N)算法耗时{0}ms", sw.ElapsedMilliseconds);
31            Console.Read();
32        }

33
34        static int computeFast(int n)
35        {
36            if (n <= 0)
37                throw new ArgumentException();
38            long result = 1;
39            long helper = 2;
40            int m = n;
41            while (m > 0)
42            {
43                if ((m & 1== 1)
44                    result = (result * helper) % n;
45                helper = ((helper % n) * (helper % n)) % n;
46                m = m >> 1;
47            }

48            return (int)result;
49        }

50
51
52        static int computeSlow(int n)
53        {
54            if (n <= 0)
55                throw new ArgumentException();
56            long result = 1;
57            for (int i = 0; i < n; i++)
58            {
59                result = (result << 1% n;
60            }

61            return (int)result;
62        }

63    }
对于两个算法中用long去申请局部变量的原因是为了防止溢出,尽管N是一个int变量,但是由于int集合在乘法运算中并不满足闭包性质,所以只有利用long才能解决问题。至于结果由于是已经%N,因此它一定小于N,仍然是一个int变量。
主要来说,这里面运用的数学定理不多,主要就是(a*b)%n=(a%n)*(b%n)%n。如果没有这个公式,可以说由于指数运算,long也必然无法满足要求而导致溢出。
至于第一种算法,主要就是将指数N分解成为二进制的形式,利用一个helper变量来维护当前需要乘的值。利用的是公式a^(m+n)=a^m*a^n。
经过测试,当N较大时,差距将比较明显——时间复杂度的区别还是非常值得注意的。
posted @ 2009-08-18 12:45 花间蕊 阅读(869) 评论(0)  编辑

2009年8月12日 #

前段时间,我做的一个项目有一个小小的需求,即:对范型集合类型ObservableCollection<T>进行排序。ObservableCollection<>这个类型在WPF和Silverlight中非常有用,因为它实现了INotifyCollectionChanged接口,继而在进行数据绑定的时候,如果将ItemsControl的ItemsSource属性绑定到一个ObservableCollection<T>对象上,那么当这个集合变化的时候(Add, Remove, Insert, Clear等等),相应的ItemsControl也会同步Update。由于ObservableCollection<T>继承自Collection<T>,因此它没有List<T>提供的Sort方法,所以如果我们想对ObservableCollection进行排序的话,就需要自己实现。
针对这个需求,我想到两个方案解决,方案一就是写一个子类,继承ObservableCollection,进而在其中实现Sort方法,相应的代码如下:
Code
方案二是为ObservableCollection<T>添加扩展方法,相应的代码如下:
Code
注:以上的代码只是例子,并没有对Argument进行应有的check,也没有考虑运行时是否会出现其他异常。
这两个方案都可以满足需求,那么就需要对它们的进行一下比较以便采取更优秀的方案。结合以上的案例,我主要是以下几点进行比较(当然也有一些并不适用于以上例子,我也一并比较了):
1)针对程序集引用与命名空间合并:
无论是继承还是扩展方法,如果被继承和扩展的类不在我们的程序集中,那么为了使用扩展的新功能,我们就必须引用这个新的程序集,同时我们可以在新的程序集中把命名空间写成和原有类所在的命名空间一样,因此在这一点上,二者是平手。
2)针对sealed类:
由于sealed类不能再被继承,因此在这种情况下我们如果需要为某一个密封类扩展功能,那么只能考虑扩展方法,在这次较量中,扩展方法+1
3)针对public, protected, internal, private成员:
扩展方法其实只是语法糖,这点想必大家都清楚,因此在我们使用扩展方法的时候,是无法获得对象private和protected成员的使用权限的,如果不在一个程序集中,internal也将变为不可见。相比之下,继承的优势在于可以访问到protected成员,因此在此役中继承+1
4)针对多态:
扩展方法只能对类扩展public成员,因此只能够能过方法的overload来实现静态多态特性,虽然通过this关键字传入方法体的对象能够在调用上使用override方法,但是您无永远在扩展方法中实现override一个方法,而继承则可以对基类中的abstract和virtual成员进行override,从而获得动态多态特性,当然overload也成了小儿科。因此在此役中继承+1
5)针对接口和抽象类的扩展方法:
C#3.0多出来的Linq命名空间下为IEnumerable接口添加了N多的扩展方法,虽然如果搞不清楚乱用会造成很多问题,但是它们的确让我们的开发变得简单明了。如果对于接口和抽象类实现扩展方法,那么受益者将会很多,凡是实现了该接口的类都将拥有这些扩展方法,而继承(这里对于接口应该不能称作继承,而是implement)却无法达到这一效果。当然有些功能我们只希望某一个类或者某一些类才有,而不是像扩展方法这样让所有抽象类的子类和实现接口的类都具有这个功能,但是在这次比较中,扩展方法仍然更胜一筹,扩展方法+1
6)针对代码的扩展性和维护性:
有很多时候,代码已经写好才突然出现某一些附加的需求,比如我已经写好了数据绑定的逻辑,用的正是ObservableCollection,如果我需要用继承一个子类扩展Sort方法,那么我将需要更改很多处代码。甚至有些时候代码是别人写的,已经编译成程序集来给你用,你可以在他的代码里得到ObservableCollection对象,却再也无法将这个对象变成SortableObservableCollection使用了。因此在这种情况下,除非反编译已经生成的程序集,不然就只能通过扩展方法实现了。而且如果再有新的需求,我们完全可以再写一个static class加入新的扩展方法,这一点继承一个子类是无法满足需要的。因此,此役中扩展方法+1
7)关于反射:
大家都知道反射在某些时候是一种很方便的手段,或者说是不得不用的手段。在有反射参与的情况下,扩展方法可以说没了用武之地,比如刚才那个ObservableCollection,我得到typeof(ObservableCollection)之后,仍然无法得到Sort方法的MethodInfo,因此也无法Invoke,原因是那个Sort方法是定义在ObservableCollectionExtension这个静态类当中的,如果我想用Sort方法就必须反射ObservableCollectionExtension,这对于使用者是极为不便的。然后对于继承,这个问题就不会出现,我当然能够在SortableObservableCollection的Type中找到Sort方法对应的MethodInfo。因此,在有反射参与的情况下,继承+1
8)关于面向对象:
我一直觉得面向对象是仁者见仁,智者见智的东西,而且不同的场合不同的应用对于两个方案可能会有不同的选择。按照道理说,无疑继承更加面向对象一点,当然这只是我个人的感觉。其实上面提到过扩展方法也能够在方法调用上体现多态,只不过是它无法override方法罢了。因此在这个比较上,继承子类+0.5

通过上面的几项比较,其实继承和扩展方法两者实在是难分伯仲。我觉得在选择的时候还是要具体问题具体分析的,比如上面给出的有关protected和sealed这两个问题,我们就是“不得不”做出选择,而更多的情形还是需要考虑易用性以及代码美观等因素的。比如关于我那个项目,我最终选择的扩展方法,原因就是我懒得去改以前的代码了。
那么对于继承子类和扩展方法这两种方式对于扩展一个类的的比较就到此为止了,欢迎大家进行补充,并指出我的不足之处。
posted @ 2009-08-12 12:35 花间蕊 阅读(2471) 评论(10) 编辑

2009年8月8日 #

众所周知,在微软的Windows平台,我们有许许多多的桌面应用,这让我们的生活变得丰富多彩而且有趣。在BS结构应用和RIA应用大行其道的今天,有很多桌面版应用推出了ajax版本和flex版本,免安装的同时也让升级和维护变得更加容易了。
Silverlight作为RIA的一种,它的优势体现在丰富的交互性上。如果说桌面版应用的载体是Windows Explorer的话,那么Silverlight应用的载体就是Internet Explorer(用FF的同学不要骂我,我还是喜欢IE的,因为它对SL的支持更好一些)。过去的一段时间,我通过补充和修改一些SL2时代写的代码,把Explorer这个概念搬到了Silverlight上,以求摸拟WPF上Window这个类的特性。由于本人的美术功底不好没有艺术细胞,而且也不怎么会用Blend,所以做出来的UI实在是有点不太好看。不过至少基本的功能都实现得差不多了,在这里我简要地介绍一下。
对于这个项目,主要的功能几乎全部由Explorer和Form这两个Control实现,前者可以定制WallPaper, Desktop Shortcuts, TaskBar等等,并且对所有的Form对象进行统一管理;后者主要用以支持最大化、还原、最小化,拖拽,改变窗口大小和窗口位置等逻辑功能。运行时的效果大约如图所示(这个是我自己的山寨版MSN):

Explorer类与Form类的使用也相对简单,Explorer只需要写在Xaml里面,一般使其成为Application.Current.RootVisual的根元素,使其能够充满整个Silverlight应用即可。Form类大约可以像Silverlight3中推出的ChildWindow一样使用,只不过在Show之前需要设置它的Explorer属性,不然的话会出现空指针。

在这个基础上实现一个自己的Form很简单,由于Form本身是一个ContentControl,那么在Xaml中可以这样定义:
<provider:Form x:Class="SilverlightWindow.HelloWorld"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:provider
="clr-namespace:WinFormProvider.Controls;assembly=WinFormProvider"
    Width
="600" Height="400" Icon="icon.jpg" Title="Hello World" Top="100" Left="100"
    ShowInTaskBar
="True" MinWidth="500" MinHeight="300">
    
<provider:Form.TrayAreaIcon>
        
<provider:TrayAreaIcon Icon="icon.jpg" DoubleClick="TrayAreaIcon_DoubleClick"/>    
    
</provider:Form.TrayAreaIcon>
    
<Grid x:Name="LayoutRoot" Background="White">
        
<TextBlock>
            Hello, there is a form, it will show with 100px XOffset and 100px YOffset
            
<LineBreak/>
            You can resize it, and drag it, or close it
            
<LineBreak/>
            I don't konw how to create an introduction documentation
            
<LineBreak/>
            However, I think this control is quite easy, you can do it as WPF window
            
<LineBreak/>   
            
<LineBreak/>
            
<LineBreak/>
            And don't be surprised if the footer disappers from taskbar when you minimize the form
            
<LineBreak/>
            it is because I think if there is a trayarea icon, footer is useless
        
</TextBlock>
    
</Grid>
</provider:Form>
而且后台的代码中,由于开放了比较丰富的public、protected属性、方法以及事件,因此我们可以实现很多功能,具体您可以参考代码以及示例。

这个项目(包括示例)我已经将它发布到了codeplex上面:http://silverlightwindow.codeplex.com/,是一个Alpha版本。如果哪位有兴趣的话,可以联系我,大家一起把它完善了。
我仔细阅读了有关把文章放到首页的要求,感觉我这篇随笔像是属于4) 软件发布的文章,但是我希望能有更多的人看到这篇文章,如果您喜欢silverlight,觉得这个项目有价值的话,欢迎加入我,大家一起丰富和完善这个项目。
最后再小小庆祝一下本人在silverlight官网(http://silverlight.net/forums)的积分达到2000,成功晋级contributor。截图留念:
  
posted @ 2009-08-08 16:08 花间蕊 阅读(1822) 评论(23) 编辑

2009年6月25日 #

小生不才,自接触Silverlight至今已经一年有余,平时用Silverlight开发应用的时候,使用最多的是UserControl,即使用MS或者第三方提供的通用控件或者Custom Control进行组合之后用以实现某些业务逻辑。尽管也曾因为学习研究视觉状态管理而制作过几个Custom Control,但那些没有任何的实用价值。最近为了模仿WPF上的一些功能,不得不为Silverlight实现几个通用的控件,这个Menu就是其中之一。

具体的实现细节我不想多说,主要是借鉴微软的ComboBox以及TreeView两个控件的实现技巧,我尽可能的模拟了WPF中的Menu控件的继承结构以及行为。同时我还做了一个ContextMenu的控件,但是由于Silverlight本身不支持右键事件,即使我们用JS侦听右键点击也无法做到消息的路由,因此这个ContextMenu控件并没有什么价值。

这套通用控件是用Silverlight 3beta开发的,开发工具为Visual Studio 2008sp1,测试环境为IE7。由于项目中几乎没有用到SL3的新功能,所以仍然使用Silverlight 2RTW的朋友们可以新建一个Silverlight class library,将代码和generic.xaml全部copy过去,重新Build即可(注意一下Silverligh 2中没有Effect这个东西,它是SL3新增的,把它去掉就可以了)。

这个Menu和TreeView的使用方法基本类似,可以直接在xaml中写MenuItem,也可以使用数据绑定来生成MenuItem,我在项目中加了一个示例,演示如何使用这两种方式生成您的Menu。同时Menu也支持子菜单,从一级菜单到N级菜单都支持。

我的测试不怎么周全,也许还会有一些问题。如果您发现问题,欢迎给我留言或者发Email给我(13811125661@vip.163.com)。您可以下载源代码,也可以根据您的需要对它的功能进行添加或者修改。

Silverlight2RTW版源代码点我下载

Silverlight3Beta版源代码点我下载

 Silverlight3RTW版源代码点我下载

我将这个项目发布到了CodePlex上(http://sl3menu.codeplex.com/),那边不写发布Licence是不让发布的,我就跟风选了一个Ms-PL,虽然这里面写的什么我都没读懂,汗!

最后截两张运行时的图吧:


posted @ 2009-06-25 14:49 花间蕊 阅读(3237) 评论(18) 编辑

2009年6月5日 #

摘要: .net作为托管乎台,释放内存和指针的任务已经交由虚拟机(CLR)来管理了,即所谓的GC。.net对于什么样的对象已经是垃圾说得很清楚,即“对象已经不可达”,这一点还算比较好理解,运行时所有的对象组织成为一张图,而在图论中,“可达性”是作为一个非常重要的性质来进行讨论的。虽然GC的机制却有很多东西并没有告诉我们,但是的确那些东西不需要我们太过于关心,...阅读全文
posted @ 2009-06-05 13:42 花间蕊 阅读(210) 评论(0) 编辑

2009年4月9日 #

摘要: 微软公司,我想无需我多说什么,百分之九十以上的中国网民,都用着盗版的Windows操作系统。对于软件行业的从业人员来说,微软恐怕也是经常需要提及的名词。再具体些,和这篇的题目联系上,就是对于程序员,微软这个世纪推出了.net这套傻瓜型语言,和Java很相似,但更更容易上手。对于一个没有任何编程基础的人(即没学过任何程序语言、数据结构、编译原理、算法和操作系统等相关知识的人),他第一天接触C#.ne...阅读全文
posted @ 2009-04-09 13:45 花间蕊 阅读(354) 评论(5) 编辑

2009年3月18日 #

摘要: 抱歉我用了一个这么“二”的题目,不过二点就二点吧,希望内容还不算太二。其实学习过编程的同学,都对这三个东西再熟悉不过了。int,又称作整型,在.net中特指的是Int32,为32位长度的有符号整型变量。float,单精度浮点数,32位长度,1位符号位,8位指数位与23位数据位,在.net中又称为Single。double,64位长度的双精度浮点数,1位符号位,11位指数位,...阅读全文
posted @ 2009-03-18 10:43 花间蕊 阅读(4041) 评论(2) 编辑

2009年2月18日 #

摘要: 众所周知,Silverlight默认并不支持右键点击和滚轮操作。微软曾经在它的DeepZoom解决方案中给出了一套对于滚轮操作的支持方案(包括一个Helper类,其实中含有NestedType,还有一个EventArgs类),但是至今仍然没有比较正式的对于右键的支持,所以我依照微软的思路,做了对于右键支持的通用类,希望能够对大家有所帮助。今天要介绍的,是这个通用方法的全局侦测的版本,即无论用户点击...阅读全文
posted @ 2009-02-18 10:19 花间蕊 阅读(478) 评论(1) 编辑