免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1332 | 回复: 0
打印 上一主题 下一主题

浅谈非主流多态 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-11-07 16:32 |只看该作者 |倒序浏览
浅谈非主流多态





在一些不基于传统OOP模型的编程语言中,由于没有或者不强调类与对象的关系,实现多态的方法并不能够通过类间的继承或者接口来实现。这种情况下也许函式编程和元编程的思想能给我们一点启发,本文用了没有类也没有接口的JavaScript作为用例来表述。示例代码中只包含了最基本的逻辑,不包含太多的容错处理,代码在Node.js下运行通过。



最大值最小值
我们可以先从一个最简单的问题开始着手:查找一堆数字中的最大值和最小值。这里假设把一堆整数放到数组中并用擂台算法的实现,其实查找最大值和最小值两个动作的其它部份逻辑都是相同的,唯一不同的就是比较两个值大小的逻辑。在找最大值的时候,我们要判断新来的值是否比较大;在找最小值时则相反。



先看代码:

Js代码
  1. 1.var numbers = [2,3,1,5,4];   
  2. 2.  
  3. 3.var maximum = function(max, current) {   
  4. 4.    return max > current ? max : current;   
  5. 5.};   
  6. 6.var minimum = function(min, current) {   
  7. 7.    return min < current ? min : current;   
  8. 8.};   
  9. 9.  
  10. 10.console.log(numbers.reduce(maximum));   
  11. 11.console.log(numbers.reduce(minimum));   
  12. 12.console.log(numbers);  
  13. var numbers = [2,3,1,5,4];

  14. var maximum = function(max, current) {
  15.     return max > current ? max : current;
  16. };
  17. var minimum = function(min, current) {
  18.     return min < current ? min : current;
  19. };

  20. console.log(numbers.reduce(maximum));
  21. console.log(numbers.reduce(minimum));
  22. console.log(numbers);
复制代码
以上代码输出结果:

5
1
[ 2, 3, 1, 5, 4 ]


我们定义了maxinum和mininum两个函数,它们都接受两个值:目前为止的最大值,和一个还没进行比较的新值。然后执行对比的逻辑,并返回更大或者更小的一个。



我们用到了ES5标准中的Array.prototype.reduce函数,对整数组进行递减运算,并把用于计算最大值或最小值的函数作为参数传到reduce中(类似于事件编程中的回调函数)。在reduce开始执行时,它会把上一次reduce的值传进去作为函数的第一个参数,再把当前正在处理的值传为第二个参数。由于每一次reduce执行时,都是设用maximum或者mininum来得到一个结果,所以实际上,我们的操作是把每一轮选出来比较大的数,和数组中下一个数进行比较,以此类推。



关于reduce的详情,可参见:https://developer.mozilla.org/en ... bjects/Array/Reduce



排序
又如排序问题也基本相同,升序和降序的逻辑也绝大部份相同,只是比较两个数大小的那部份不一样。



先看代码:

Js代码
  1. 1.var numbers = [2,3,1,5,4];   
  2. 2.  
  3. 3.var ascendant = function(left, right) {   
  4. 4.    if (left == right) return 0;   
  5. 5.    return left > right ? 1 : -1;   
  6. 6.};   
  7. 7.var descendant = function(left, right) {   
  8. 8.    if (left == right) return 0;   
  9. 9.    return left < right ? 1 : -1;   
  10. 10.};   
  11. 11.  
  12. 12.console.log(numbers.concat().sort(ascendant));   
  13. 13.console.log(numbers.concat().sort(descendant));   
  14. 14.console.log(numbers);  
  15. var numbers = [2,3,1,5,4];

  16. var ascendant = function(left, right) {
  17.     if (left == right) return 0;
  18.     return left > right ? 1 : -1;
  19. };
  20. var descendant = function(left, right) {
  21.     if (left == right) return 0;
  22.     return left < right ? 1 : -1;
  23. };

  24. console.log(numbers.concat().sort(ascendant));
  25. console.log(numbers.concat().sort(descendant));
  26. console.log(numbers);
复制代码
以上代码的输出结果:

[ 1, 2, 3, 4, 5 ]
[ 5, 4, 3, 2, 1 ]
[ 2, 3, 1, 5, 4 ]
  
在代码中定义了ascendant和descendant两个函数,它们都可接受两个参数,并跟据传入的两个数的大小情况,返回相应的0、-1或者1。这两个函数目前只处理数字类型,我们会在下一节描述如何处理复合数据。



在ES3中已经定义了一个Array.prototype.sort函数用于排序数组,该函数接受另一个对比函数作为它的第一个参数,并且会把邻近的两个值传到这个对比函数中,由这个对比函数执行比较两个数大小的逻辑,最后跟据返回值是否等于0、大于0或小于0三种情况来决定如何排序数组。



在调用sort之前还执行了一次concat,这是一个用于复制数组却又想偷懒时用的小技巧。因为sort会改变调用它的数组的顺序,所以要复制一份以保证原数组不被修改。这也是函式编程的思想之一,避免外部状态和可变数组对函数返回结果所造成的影响。



关于sort的详情,可参见:https://developer.mozilla.org/en ... _Objects/Array/sort



复合数据类型排序
在实现应用中,经常会遇到一个由复合数据类型组成的数组,比如说对象数组。这时可能需要根据每个对象中的某个值进行排序。如以下代码中有若干玩家players对象,要对攻击力offensive进行升序排列,以及对防御力defensive进行降序排列。



代码如下:

Js代码
  1. 1.var players = [   
  2. 2.    {offensive: 2, defensive: 5},   
  3. 3.    {offensive: 1, defensive: 1},   
  4. 4.    {offensive: 3, defensive: 3},   
  5. 5.    {offensive: 5, defensive: 2},   
  6. 6.    {offensive: 4, defensive: 4},   
  7. 7.];   
  8. 8.  
  9. 9.var sortBy = function(fieldName, orderDirection) {   
  10. 10.    var field = fieldName;   
  11. 11.    var order = orderDirection < 0 ? -1 : 1;   
  12. 12.    return function(left, right) {   
  13. 13.        if (left[field] == right[field]) return 0;   
  14. 14.        return (left[field] > right[field] ? 1 : -1) * order;   
  15. 15.    };   
  16. 16.};   
  17. 17.  
  18. 18.console.log(players.concat().sort(sortBy("offensive", 1)));   
  19. 19.console.log(players.concat().sort(sortBy("defensive", -1)));   
  20. 20.console.log(players);  
  21. var players = [
  22.     {offensive: 2, defensive: 5},
  23.     {offensive: 1, defensive: 1},
  24.     {offensive: 3, defensive: 3},
  25.     {offensive: 5, defensive: 2},
  26.     {offensive: 4, defensive: 4},
  27. ];

  28. var sortBy = function(fieldName, orderDirection) {
  29.     var field = fieldName;
  30.     var order = orderDirection < 0 ? -1 : 1;
  31.     return function(left, right) {
  32.         if (left[field] == right[field]) return 0;
  33.         return (left[field] > right[field] ? 1 : -1) * order;
  34.     };
  35. };

  36. console.log(players.concat().sort(sortBy("offensive", 1)));
  37. console.log(players.concat().sort(sortBy("defensive", -1)));
  38. console.log(players);
复制代码
以上代码输出结果:
  1. [ { offensive: 1, defensive: 1 },
  2. { offensive: 2, defensive: 5 },
  3. { offensive: 3, defensive: 3 },
  4. { offensive: 4, defensive: 4 },
  5. { offensive: 5, defensive: 2 } ]
  6. [ { offensive: 2, defensive: 5 },
  7. { offensive: 4, defensive: 4 },
  8. { offensive: 3, defensive: 3 },
  9. { offensive: 5, defensive: 2 },
  10. { offensive: 1, defensive: 1 } ]
  11. [ { offensive: 2, defensive: 5 },
  12. { offensive: 1, defensive: 1 },
  13. { offensive: 3, defensive: 3 },
  14. { offensive: 5, defensive: 2 },
  15. { offensive: 4, defensive: 4 } ]
复制代码
sortBy这个函数可以跟据传入的两个参数:用于排序的属性,以及升序1还是降序-1,来动态生成一个提供给sort函数的用的比较两个对象的函数。



这个示例中同时用了函式编程和元编程的思想:把函数作为数据,在运行期动态生成和传递。这样利用了动态脚本的灵活性,达到了实现多态的目的,同时也不需要使用传统OOP中过于精致和复杂的类和接口的设计。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP