用户操作
[留言]  [发消息]  [加为好友] 
订阅我的博客
XML聚合    FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
pongba的公告
除非特别声明,本站采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" rel="license"><img width="16" height="16" border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_o_cclogo.gif" alt="Creative Commons License"/></a>许可。转载请保留作者、出处。非商业。 <br/> <h3>重要公告</h3> <div style="background:#000000;color:#ffffff;">本博客已经迁移至 http://mindhacks.cn ,此处保留作为镜像,但不保证一定同步更新所有内容。原订阅 http://blog.csdn.net/pongba/rss.aspx (原始 Feed) 的朋友请转为订阅永久 feed : http://mindhacks.cn/feed/</div> <h3>关于</h3> 我经常在 @<a href="https://groups.google.com/group/pongba" target="_blank">TopLanguage</a> | @<a href="https://twitter.com/pongba" target="_blank">Twitter</a> | @<a href="http://www.douban.com/people/pongba/" target="_blank">Douban</a> <br/><br/> <style type="text/css"> #accordionmenu ul{ list-style-type: none; } #accordionmenu li{ list-style-type: none; padding: 0px; } #accordionmenu ul, #accordionmenu ul ul { list-style: none; margin: 0; padding: 0; } #accordionmenu ul li { display: inline; } #accordionmenu ul a { display: block; text-decoration: none; } #accordionmenu ul li ul li{ vertical-align: bottom; } #accordionmenu ul li a { background: #333; color: #fff; padding: 0.5em; } #accordionmenu ul li a:hover { background: #000; } #accordionmenu ul li a, #accordionmenu ul li a:hover { border-bottom: 1px dotted #C0C0C0; } #accordionmenu ul li ul li a { background: #ccc; color: #000; padding-left: 20px; } #accordionmenu ul li ul li a:hover { background: #aaa; border-left: 5px #000 solid; padding-left: 15px; } #accordionmenu h3 { font-weight: bold; text-align: center; font-size: 14pt; border-bottom: none; } #accordionmenu h1 { font-size: 14px; font-weight: bold; text-align: center; } #accordionmenu p { text-align: right; } .sharing { border:1px dotted #AAAAAA; padding:10px; } </style> <div id="accordionmenu"> <h3>《C++的罗浮宫》5年选集</h3> <p>——知识分享是最大的复用</p> 下载地址:<a href="http://download.csdn.net/source/630320" target="_blank">csdn资源频道</a>|<a href="http://www.mediafire.com/?r9y9aynxy84" target="_blank">mediafire</a> <ul> <li> <a target="_blank" href="http://del.icio.us/pongbablog/machine-learning">机器学习与人工智能</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2008/09/21/2958094.aspx">数学之美番外篇:平凡而又神奇的贝叶斯方法</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/09/11/2915005.aspx">机器学习与人工智能学习资源导引</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/essay">学习与思考</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2008/12/18/3549560.aspx">如何清晰地思考:近一年来业余阅读的关于思维方面的知识结构整理(附大幅思维导图)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2009/01/14/3776586.aspx">什么是你的不可替代性和核心竞争力</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2009/01/16/3796771.aspx">锤子和钉子</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2009/01/18/3829054.aspx">逃出你的肖申克(一):一定要亲身经历了之后才能明白?</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/07/08/2625115.aspx">一直以来伴随我的一些学习习惯(part1)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/07/20/2681668.aspx">一直以来伴随我的一些学习习惯(part2)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/09/17/2942482.aspx">一直以来伴随我的一些学习习惯(part3)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/12/05/3456240.aspx">一直以来伴随我的一些学习习惯(part4)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/10/29/3176250.aspx">方法论、方法论——程序员的阿喀琉斯之踵</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/07/07/2622713.aspx">知其所以然地学习(以算法学习为例)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/04/08/2260812.aspx">阅读与思考</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/03/03/2143245.aspx">Failing To See the Big Picture - Mistakes we make when learning programming</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/02/25/2118907.aspx">你的技术之路</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/01/04/2025830.aspx">鱼是最后一个看到水的</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/08/10/1736333.aspx">玩的岁月</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/06/24/1664597.aspx">我不想与我不能</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/05/24/1624382.aspx">学习密度与专注力</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/problem-solving">解题与思维</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2008/04/18/2302905.aspx">跟波利亚学解题(rev#3)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/06/05/2513263.aspx">学习与记忆</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/05/07/2412144.aspx">解题思维杂感三则</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/06/05/2513247.aspx">动态规划与排列组合</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/04/09/2270171.aspx">今天我们思考</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/math">数学与计算理论</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2006/10/15/1336028.aspx">康托尔、哥德尔、图灵——永恒的金色对角线(rev#2)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/12/02/1912466.aspx">数学之美番外篇:进化论中的概率论</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/06/13/2544933.aspx">数学之美番外篇:快排为什么那样快</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/cplusplus">C++与技术</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2007/09/11/1780545.aspx">为什么C++</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/12/04/1916385.aspx">为什么C++(C++之父的观点)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/12/11/1930150.aspx">学习C++:实践者的方法</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/05/16/1611593.aspx">你应当如何学习C++(以及编程)(rev#1)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/01/10/2034207.aspx">C++之父元旦专访(8+13个问题,关于C++的学习使用和未来)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/08/08/1732055.aspx">争论C++前你应当知道什么(rev#1)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/10/08/1815742.aspx">错误处理:为何、何时、如何(rev#2)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2006/01/26/588638.aspx">锁无关的数据结构</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2006/01/29/589864.aspx">锁无关的数据结构与Hazard指针</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/06/13/2544894.aspx">泛型编程:源起、实现与意义</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/07/29/1715263.aspx">Generic Programming - What are you, anyway?</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2004/09/01/90642.aspx">深度探索元函数</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2003/10/24/19130.aspx">为什么C++编译器不能支持对模板的分离式编译</a></li> </ul> </li> <li> <a href="http://blog.csdn.net/pongba/articles/1561194.aspx">《C++0x漫谈》系列</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2007/08/04/1726031.aspx">《C++0x漫谈》之:Concept! Concept!</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/09/10/1778748.aspx">《C++0x漫谈》之:瘦身前后——兼谈语言进化</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/09/07/1776255.aspx">《C++0x漫谈》之:Auto的故事</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/07/10/1684519.aspx">《C++0x漫谈》之:Move语意与完美转发(上)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/07/18/1697636.aspx">《C++0x漫谈》之:Move语意与完美转发(下)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/06/20/1659952.aspx">《C++0x漫谈》之:多线程内存模型</a></li> </ul> </li> <li> <a href="http://blog.csdn.net/pongba/articles/1561110.aspx">《Boost源码剖析》系列</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1561006.aspx">《Boost源码剖析》之:多重回调机制signal(上)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1561083.aspx">《Boost源码剖析》之:多重回调机制signal(下)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1560773.aspx">《Boost源码剖析》之:泛型回调function(rev#3)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1560754.aspx">《Boost源码剖析》之:Tuple Types(rev#2)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1560738.aspx">《Boost源码剖析》之:泛型多维数组multi_array</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2004/08/24/82811.aspx">《Boost源码剖析》之:泛型指针any(rev#2)</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/essay">其它</a> <ul> <li><a href="http://del.icio.us/pongbablog/%E5%8F%A4%E9%BE%99">读古龙的岁月</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/10/07/1813553.aspx">我们为什么这么容易受骗?</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/26/1586144.aspx">Blog外挂:妙用del.icio.us实现“站内相关文章”</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/03/12/2172677.aspx">找啊找啊找朋友</a></li> </ul> </li> <li> <a href="http://blog.csdn.net/pongba/MyArticles.aspx">所有文章</a> </li> </ul> </div> <br/> <style type="text/css"> .delicious-posts ul {list-style-type:none; margin: 0 0 1em 0; padding: 0 } .delicious-extended{margin:0;padding:0 0 0.25em 0} .module-list-item .delicious-posts ul{margin:0;padding:0} .module-list-item .delicious-posts h2, .module-list-item .delicious-posts li:first-child{margin-top:0} </style> <h3>讨论问题请到<a href="http://groups.google.com/group/pongba">TopLanguage</a>综合技术讨论组</h3> <center><a href="http://groups.google.com/group/pongba"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_01-sm-c.gif" alt="TopLanguage"></img></a></center> <br/> <h3>精彩言论@TopLanguage</h3> <div id="toplang_comments" class="sharing"> </div> <br/> <h3>pongba的共享阅读<a href="http://delicious.com/pongba" target="_blank">@Delicious</a></h3> <div id="delicious_shared_reading" class="sharing"> </div> <br/> <h3>pongba<a href="http://twitter.com/pongba" target="_blank">@Twitter</a></h3> <div id = "twitter_update_list" class="sharing"> </div> <script type="text/javascript"> function twitterCallback2(C){var A=[];for(var D=0;D<C.length;D++){var E=C[D].user.screen_name;var B=C[D].text.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g,function(F){return'<a href="'+F+'">'+F+"</a>"}).replace(/\B@([_a-z0-9]+)/ig,function(F){return F.charAt(0)+'<a href="http://www.twitter.com/'+F.substring(1)+'">'+F.substring(1)+"</a>"});A.push("<li><span>"+B+'</span> <a style="font-size:85%" href="http://twitter.com/'+E+"/statuses/"+C[D].id+'">'+relative_time(C[D].created_at)+"</a></li>")}document.getElementById("twitter_update_list").innerHTML=A.join("")}function relative_time(C){var B=C.split(" ");C=B[1]+" "+B[2]+", "+B[5]+" "+B[3];var A=Date.parse(C);var D=(arguments.length>1)?arguments[1]:new Date();var E=parseInt((D.getTime()-A)/1000);E=E+(D.getTimezoneOffset()*60);if(E<60){return"less than a minute ago"}else{if(E<120){return"about a minute ago"}else{if(E<(60*60)){return(parseInt(E/60)).toString()+" minutes ago"}else{if(E<(120*60)){return"about an hour ago"}else{if(E<(24*60*60)){return"about "+(parseInt(E/3600)).toString()+" hours ago"}else{if(E<(48*60*60)){return"1 day ago"}else{return(parseInt(E/86400)).toString()+" days ago"}}}}}}}; </script> <br/> <h3>pongba在读<a href="http://www.douban.com/people/pongba/" target="_blank">@豆瓣</a></h3> <script type="text/javascript" src="http://www.douban.com/service/badge/pongba/?show=dolist&amp;select=favorite&amp;n=9&amp;columns=3&amp;cat=book" ></script> <br/> <h3>gtalk/msn(邮件请发送到gmail邮箱)</h3> <a href="mailto:pongba@gmail.com"> <img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_gmail.gif" alt="pongba@gmail.com"></img> </a> <br/> <a href = "mailto:pp_liu@msn.com"> <img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_msn.gif" alt="pp_liu@msn.com"></img> </a> <br/> <h3>搜索(不要回车,点击Go)</h3> <script language="javascript"> function search () { var wd=document.getElementsByName("q")[0].value; var link="http://www.baidu.com/s?ie=utf-8&tn=baidulocal&cl=3&ct=2097152&si=blog.csdn.net%2Fpongba" + "&wd=" + wd ; window.open(link); } </script> <input type="text" onkeydown="javascript:var kCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode; if (kCode == 13){search();}" name="q"/> <input type="button" onclick="javascript:search()" value="Go"/> <br/> <h3>pongba翻译的</h3> <br/> <center> <a href="http://blog.csdn.net/pongba/archive/2007/12/03/1914425.aspx" target="_blank"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_bc_small.JPG"></img></a> </center> <br/> <center> <a href="http://blog.csdn.net/pongba/archive/2007/03/09/1525361.aspx" target="_blank"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_wewlc_small.jpg"></img></a> </center> <br/> <center> <a href="http://blog.csdn.net/pongba/archive/2005/11/22/534336.aspx" target="_blank"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_ecs_small.JPG"></img></a> </center> <br/> <center> <a href="http://blog.csdn.net/pongba/archive/2005/08/15/455350.aspx" target="_blank"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_ic_small.JPG"></img></a> </center> <script type="text/javascript"> (function(){ var toplang = document.createElement('div'); toplang.setAttribute('id', 'toplang'); toplang.setAttribute('style', 'float:right; margin:0pt 1em; position:absolute; right:2%; top:15px;') var a1 = document.createElement('a'); a1.setAttribute('href', 'http://groups.google.com/group/pongba'); a1.setAttribute('target', '_blank'); a1.setAttribute('title', 'TopLanguage'); a1.setAttribute('style', 'text-decoration: underline;'); a1.appendChild(document.createTextNode('TopLanguage讨论组')); toplang.appendChild(a1); toplang.appendChild(document.createTextNode(' | ')); a1 = document.createElement('a'); a1.setAttribute('href', 'http://groups.google.com/group/pongba/web/toplanguage-subscription-howto'); a1.setAttribute('target', '_blank'); a1.setAttribute('title', '加入TopLanguage'); a1.setAttribute('style', 'text-decoration: underline;'); a1.appendChild(document.createTextNode('加入')); toplang.appendChild(a1); var header = document.getElementById('csdnblog_header'); if(header) { header.appendChild(toplang); } })(); </script> <script type="text/javascript"> /* test if _item_ is in _set_ */ function IsIn(item, set) { for(var i = 0; i < set.length; ++i){ if(set[i]&&(item == set[i]))return true; } return false; } /* get the link to the current page, strip out the # part if the page is opened through a comment link */ function GetThisURL() { var thisURL = document.URL; var end = thisURL.indexOf('#'); if(end != -1){ thisURL = thisURL.slice(0, end); } return thisURL; } /* select from _set_ all the elements that don't exist in _filter_ */ function Unique(set, filter) { var newSet = new Array(); for(var i = 0; i < set.length; i++){ if(!IsIn(set[i], filter)) newSet.push(set[i]); } return newSet; } /* randomly select _num_ items from _set_ */ function RandomSelection(set, num) { var selected = new Array(); var positiveSelecting = (num <= set.length/2); var ajustedNum = (positiveSelecting ? num : (set.length - num)); for(var n = 0; n < ajustedNum;){ var randIdx = Math.floor(Math.random()*(set.length)); if(!IsIn(set[randIdx], selected)){ selected.push(set[randIdx]); n++; } } if(!positiveSelecting){ selected = Unique(set, selected); } return selected; } function getElements(classname, tagname, root) { // If no root was specified, use the entire document // If a string was specified, look it up if (!root) root = document; else if (typeof root == "string") root = document.getElementById(root); // if no tagname was specified, use all tags if (!tagname) tagname = "15-"; // Find all descendants of the specified root with the specified tagname var all = root.getElementsByTagName(tagname); // If no classname was specified, we return all tags if (!classname) return all; // Otherwise, we filter the element by classname var elements = []; // Start with an empty array for(var i = 0; i < all.length; i++) { var element = all[i]; if (isMember(element, classname)) // isMember() is defined below elements.push(element); // Add class members to our array } // Note that we always return an array, even if it is empty return elements; // Determine whether the specified element is a member of the specified // class. This function is optimized for the common case in which the // className property contains only a single classname. But it also // handles the case in which it is a list of whitespace-separated classes. function isMember(element, classname) { var classes = element.className; // Get the list of classes if (!classes) return false; // No classes defined if (classes == classname) return true; // Exact match // We didn't match exactly, so if there is no whitespace, then // this element is not a member of the class var whitespace = /\s+/; if (!whitespace.test(classes)) return false; // If we get here, the element is a member of more than one class and // we've got to check them individually. var c = classes.split(whitespace); // Split with whitespace delimiter for(var i = 0; i < c.length; i++) { // Loop through classes if (c[i] == classname) return true; // and check for matches } return false; // None of the classes matched } } var HTTP = {}; HTTP._factories = [ function() { return new XMLHttpRequest(); }, function() { return new ActiveXObject("Msxml2.XMLHTTP"); }, function() { return new ActiveXObject("Microsoft.XMLHTTP"); } ]; HTTP._factory = null; HTTP.newRequest = function() { if (HTTP._factory != null) return HTTP._factory(); for(var i = 0; i < HTTP._factories.length; i++) { try { var factory = HTTP._factories[i]; var request = factory(); if (request != null) { HTTP._factory = factory; return request; } } catch(e) { continue; } } HTTP._factory = function() { throw new Error("XMLHttpRequest not supported"); } HTTP._factory(); } </script> <script type="text/javascript"> function showTopPosts(availElem, suffix) { if(!availElem)return; availElem.parentNode.insertBefore(document.createElement('br'), availElem); var topPostsDiv = document.createElement('div'); topPostsDiv.setAttribute('id', 'topPosts'+suffix); topPostsDiv.innerHTML = '<div style="margin: 0px 3% 0px 3%;background:#F9F9F9 none repeat scroll 0% 0%; border:1px solid #CCCCCC; color:#5B5B5B; font-size:10pt; font-style:normal; line-height:150%; padding:0px 10px;">' + '<table width="100%"><tbody><tr valign="middle"><td width="12" style="font-weight: bold;">置<br/>顶<br/>文<br/>章</td>' + '<td><ul id="' + 'recomm1' + suffix + '"></ul></td><td><ul id="' + 'recomm2' + suffix + '"></ul></td>' + '</tr></tbody></table></div>'; availElem.parentNode.insertBefore(topPostsDiv, availElem); var numTopPosts = 0; var i = 0; for(i = 0 ; i < Delicious.posts.length; ++i){ if(IsIn('topPost', Delicious.posts[i].t)){ numTopPosts++; } } var cond1 = document.getElementById('recomm1'+suffix); cond1.innerHTML = ''; var count = 0; for(i = 0; count < numTopPosts/2; ++i){ if(IsIn('topPost', Delicious.posts[i].t)){ count++; cond1.innerHTML += '<li><a href = "' + Delicious.posts[i].u + '">' + Delicious.posts[i].d + '</a></li>'; } } var cond2 = document.getElementById('recomm2'+suffix); cond2.innerHTML = ''; for(; i < Delicious.posts.length; ++i){ if(IsIn('topPost', Delicious.posts[i].t)){ cond2.innerHTML += '<li><a href = "' + Delicious.posts[i].u + '">' + Delicious.posts[i].d + '</a></li>'; } } availElem.parentNode.insertBefore(document.createElement('br'), availElem); } function doShowTopPosts() { showTopPosts(document.getElementById('Post.ascx_ViewPost_PreviousAndNextEntriesUp'), 'Up'); showTopPosts(document.getElementById('Post.ascx_ViewPost_PreviousAndNextEntriesDown'), 'Down'); } </script> <script type="text/javascript"> function urlToId(url) { var a1 = url.split('/'); var a2 = a1[a1.length - 1]; var a3 = a2.split('.'); return a3[0]; } var getFeedbackCountReq; var myBlogPosts = {}; var hotPostsContainer; function showHotPosts(ct) { hotPostsContainer = ct; for(i = 0; i < Delicious.posts.length; ++i){ var postID = urlToId(Delicious.posts[i].u); myBlogPosts[postID] = {url:Delicious.posts[i].u, title:Delicious.posts[i].d, feedbackCount:0}; } var idList = ''; for(var id in myBlogPosts){ idList += id + ','; } getFeedbackCountReq = HTTP.newRequest(); getFeedbackCountReq.onreadystatechange = handleGetFeedbackCountReq; getFeedbackCountReq.open('GET', '/NewCount.aspx?FeedbackCountStack=' + idList); getFeedbackCountReq.send(null); } function handleGetFeedbackCountReq() { if(getFeedbackCountReq.readyState==4){ if(getFeedbackCountReq.status==200){ handleGetFeedbackCountReq2(getFeedbackCountReq.responseText); } } } function handleGetFeedbackCountReq2(feedbackStr) { var feedbackList = feedbackStr.split(','); var i; for(i = 0; i < feedbackList.length; ++i){ var IDFeedbackCountPair = feedbackList[i].split('='); myBlogPosts[IDFeedbackCountPair[0]].feedbackCount = IDFeedbackCountPair[1]; } var myBlogPostsVector = []; for(var id in myBlogPosts){ myBlogPostsVector.push({"id":id, "url":myBlogPosts[id].url, "title":myBlogPosts[id].title, "feedbackCount":myBlogPosts[id].feedbackCount}); } myBlogPostsVector.sort(compareByFeedbackCount); var title = document.createElement('h3'); title.appendChild(document.createTextNode('本blog评论最多文章')); hotPostsContainer.appendChild(title); var NGList = ['http://blog.csdn.net/pongba/archive/2004/08/26/84978.aspx', 'http://blog.csdn.net/pongba/archive/2004/11/26/195052.aspx', 'http://blog.csdn.net/pongba/archive/2004/11/26/195075.aspx']; var maxNumHotPosts = 12 + NGList.length; if(myBlogPosts.length < maxNumHotPosts) maxNumHotPosts = myBlogPosts.length; var ul = document.createElement('ul'); for(i = 0; i < maxNumHotPosts; ++i){ if(IsIn(myBlogPostsVector[i].url, NGList))continue; var li = document.createElement('li'); var a = document.createElement('a'); a.setAttribute('href', myBlogPostsVector[i].url); a.appendChild(document.createTextNode(myBlogPostsVector[i].title)); li.appendChild(a); var sup = document.createElement('sup'); sup.setAttribute('style', 'color:red;'); sup.appendChild(document.createTextNode('(' + myBlogPostsVector[i].feedbackCount + ')')); li.appendChild(sup); ul.appendChild(li); } hotPostsContainer.appendChild(ul); } function compareByFeedbackCount(post1, post2) { return post2.feedbackCount - post1.feedbackCount; } function compareById(post1, post2){ return post2.id - post1.id; } </script> <script type="text/javascript"> function GetPositionalDiv() { var elems = getElements('articalinfo', 'p'); if(elems.length==0||elems.length>1)return null; return elems[0]; } /* get tags of this post */ function GetTags() { var thisURL = GetThisURL(); var tags; for(var i = 0; i < Delicious.posts.length ; i++){ var post = Delicious.posts[i]; if(post.u == thisURL){ tags = []; for(var j = 0; j < post.t.length; ++j){ if(post.t[j] == 'topPost')continue; tags.push(post.t[j]); } break; } } return tags; } /* tags are ordered by their importance, we assume that the more specific a tag is, the more important it will be, and the smaller a tag is, the more specific it will be */ function TagOrderPred(tag1, tag2) { return Delicious.tags[tag1] - Delicious.tags[tag2]; } /* find articles related to a particular _tag_ */ function BuildRelatedArticleList(tag) { var list = new Array(); for(var i = 0, post; post = Delicious.posts[i]; i++){ if(IsIn(tag, post.t) && post.u!=GetThisURL()){ list.push(post); } } return list; } function ShowRelatedArticles(ct, tags) { if(!tags)return null; var maxSize = 12; var ul = document.createElement('ul'); ul.setAttribute('id', 'my_related_posts'); var selectedPosts = new Array(); var currentSize = 0; tags.sort(TagOrderPred); // rating tags for(var k = 0; k < tags.length; k++){ var tag = tags[k]; var relatedPosts = BuildRelatedArticleList(tag); // remove those already selected ones relatedPosts = Unique(relatedPosts, selectedPosts); // if there's no related posts to this particular tag, keep going on if(relatedPosts.length == 0) continue; // make sure the total size doesn't exeed the maxSize if(currentSize + relatedPosts.length > maxSize){ var roomLeft = maxSize - currentSize; relatedPosts = RandomSelection(relatedPosts, roomLeft); } // add'em to the collection of selected posts selectedPosts = selectedPosts.concat(relatedPosts); var promptTxt = document.createTextNode('from tag '); var promptTxtDecorated = document.createElement('font'); promptTxtDecorated.setAttribute('size', '+1'); promptTxtDecorated.appendChild(promptTxt); ul.appendChild(promptTxtDecorated); var taglnk = document.createElement('a'); taglnk.setAttribute('href', 'http://del.icio.us/pongbablog/'+tag); taglnk.appendChild(document.createTextNode(tag)); var taglnkDecorated = document.createElement('font'); taglnkDecorated.setAttribute('size', '+2'); taglnkDecorated.appendChild(taglnk); ul.appendChild(taglnkDecorated); // create list element for every related posts for(var i = 0; i < relatedPosts.length; i++){ var li = document.createElement('li'); var a = document.createElement('a'); a.setAttribute('href', relatedPosts[i].u); a.appendChild(document.createTextNode(relatedPosts[i].d)); li.appendChild(a); ul.appendChild(li); } var br = document.createElement('br'); ul.appendChild(br); currentSize += relatedPosts.length; if(currentSize >= maxSize)break; } var title = document.createElement('h3'); title.appendChild(document.createTextNode("本blog相关文章")); ct.appendChild(title); ct.appendChild(ul); return selectedPosts; } function ShowRandomArticles(ct, filter) { if(!filter) filter = []; var ul = document.createElement('ul'); ul.setAttribute('id', 'my_random_posts'); var numArticles = Delicious.posts.length; var indices = new Array(); var maxNumShown = 12; if(numArticles < maxNumShown){ maxNumShown = numArticles; } for(var i = 0; i < maxNumShown;){ var randIndex = Math.floor(Math.random()*numArticles); if((Delicious.posts[randIndex].u!=GetThisURL())&& (!IsIn(randIndex, indices))&& (!IsIn(Delicious.posts[randIndex], filter))){ indices.push(randIndex); var a = document.createElement('a'); a.setAttribute('href', Delicious.posts[randIndex].u); a.appendChild(document.createTextNode(Delicious.posts[randIndex].d)); var li = document.createElement('li'); li.appendChild(a); ul.appendChild(li); i++; } } var title = document.createElement('h3'); title.appendChild(document.createTextNode("本blog随机文章")); ct.appendChild(title); ct.appendChild(ul); } function ShowTags(ct, tags) { if(!tags)return; var ul = document.createElement('ul'); ul.setAttribute('id', 'my_related_tags'); for(var i = 0; i < tags.length; i++){ var a = document.createElement('a'); a.setAttribute('href', 'http://del.icio.us/pongbablog/'+tags[i]); a.appendChild(document.createTextNode(tags[i] + '(' + Delicious.tags[tags[i]] + ')')); var aDecorated = document.createElement('font'); aDecorated.setAttribute('size', '+1'); aDecorated.appendChild(a); var li = document.createElement('li'); li.appendChild(aDecorated); li.appendChild(document.createTextNode(' ')); ul.appendChild(li); } var title = document.createElement('h3'); title.appendChild(document.createTextNode('本文相关tags')); ct.appendChild(title); ct.appendChild(ul); } function createDivWithId(id) { var element = document.createElement('div'); element.setAttribute('id', id); return element; } function Prepare() { var ct = GetPositionalDiv(); if(!ct)return false; ct.parentNode.insertBefore(createDivWithId('blog_related_posts'), ct); ct.parentNode.insertBefore(createDivWithId('blog_hot_posts'), ct); ct.parentNode.insertBefore(createDivWithId('blog_related_tags'), ct); ct.parentNode.insertBefore(createDivWithId('blog_random_posts'), ct); ct.parentNode.insertBefore(document.createElement('br'), ct); return true; } function ShowRelatedAll() { if(!Prepare())return; var tags = GetTags(); var alreadySelected = ShowRelatedArticles(document.getElementById('blog_related_posts'), tags); showHotPosts(document.getElementById('blog_hot_posts')); ShowTags(document.getElementById('blog_related_tags'), tags); ShowRandomArticles(document.getElementById('blog_random_posts'), alreadySelected); } </script> <h3>这个Blog上都写了哪些东东</h3> <div id="tags_cloud_delicious"></div> <script type="text/javascript"> function ShowTagCloud() { var ts = Delicious.tags; if(!ts)return; var html = ''; var ta = 0,tz=36; function s(a, b, i, x) { if(a>b){ var m=(a-b)/Math.log(x), v=a-Math.floor(Math.log(i)*m); }else{ var m=(b-a)/Math.log(x), v=Math.floor(Math.log(i)*m+a); } return v; } html += ('<style type="text/css">.delicious-tags{font-family:arial,sans-serif}.delicious-tags a{text-decoration:none}.delicious-tags a:hover{text-decoration:underline}.delicious-tags ul{list-style-type:none;margin:0;padding:0; text-align:justify}.delicious-cloud li{display:inline;text-align:justify;background-image:none !important;padding:0;margin:0}.delicious-cloud .delicious-tag-count{padding-left:0.2em;font-size:12px}.delicious-cloud li:before{content:"" !important}</style>'); var ca=[135,206,235], cz=[0,0,255], c=[]; html += ('<div class="delicious-tags" id="delicious-tags-pongbablog"><ul class="delicious-cloud">'); for(var t in ts){ if(t == 'topPost')continue; for(var i=0; i<3; i++) c[i]=s(ca[i], cz[i], ts[t]-ta, tz); var fs = s(12,35,ts[t]-ta,tz); html += ('<li style="font-size:'+fs+'px;line-height:1; display:inline;"><a style="color:rgb('+c[0]+','+c[1]+','+c[2]+')" href="http://del.icio.us/pongbablog/'+encodeURIComponent(t).replace('%2F','/')+'">'+t+'</a><span class="delicious-tag-count" style="font-size:12px;line-height:'+fs+'px;">('+ts[t]+')</span> </li>'); } html += ('</ul><br/>'); html += ('</div>'); document.getElementById('tags_cloud_delicious').innerHTML = html; } </script> <script type="text/javascript"> function doAllWorks(tags) { if(!tags){return;} if(typeof(Delicious) == 'undefined'||typeof(Delicious.posts) == 'undefined') { return; } Delicious.tags = tags; doShowTopPosts(); ShowRelatedAll(); ShowTagCloud(); } </script> <script type="text/javascript"> function runOnLoad(f) { if (runOnLoad.loaded) f( ); // If already loaded, just invoke f( ) now. else runOnLoad.funcs.push(f); // Otherwise, store it for later } runOnLoad.funcs = []; // The array of functions to call when the document loads runOnLoad.loaded = false; // The functions have not been run yet. // Run all registered functions in the order in which they were registered. // It is safe to call runOnLoad.run( ) more than once: invocations after the // first do nothing. It is safe for an initialization function to call // runOnLoad( ) to register another function. runOnLoad.run = function( ) { if (runOnLoad.loaded) return; // If we've already run, do nothing for(var i = 0; i < runOnLoad.funcs.length; i++) { try { runOnLoad.funcs[i]( ); } catch(e) { /* An exception in one function shouldn't stop the rest */ } } runOnLoad.loaded = true; // Remember that we've already run once. delete runOnLoad.funcs; // But don't remember the functions themselves. delete runOnLoad.run; // And forget about this function too! }; // Register runOnLoad.run( ) as the onload event handler for the window var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = runOnLoad.run; } else { window.onload = function() { runOnLoad.run(); oldonload(); }; } </script> <script type="text/javascript"> if (typeof window.Delicious == 'undefined') window.Delicious = {}; if(typeof(Delicious) == 'undefined') Delicious = {}; Delicious.Linkrolls = function() { return { render: function(o, posts) { var out = []; w = function (s) { out.push(s); }; posts.sort(function(b,a) { return ( (a.dt > b.dt) ? 1 : (a.dt < b.dt) ? -1 : 0 ); }); w('<div class="delicious-posts">'); w('<ul>'); for(var i=0,p;( i<o.count ) && ( p=posts[i] );i++){ w('<li class="delicious-post delicious-'+(i%2?'even':'odd')+'">'); w('<a class="delicious-link"' + ' href="' + p.u + '">'+this.htmlEscape(p.d)+'</a> '); if(p.n) w('<p class="delicious-extended">'+this.htmlEscape(p.n)+'</p>'); w('</li>'); } w('</ul>'); w('</div>'); return out.join(''); }, htmlEscape: function(s) { return (''+s).replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }, EOF: null }; }(); Delicious.Linkrolls_CB_51902 = function(posts) { var myhtml = Delicious.Linkrolls.render({"user":"pongba","usertags":"shared-reading","version":"2","count":"15","sort":"date","extended":true,"icon":"s","name":true,"showadd":true,"BASE_URL":"http:\/\/delicious.com","STATIC_URL":"http:\/\/l.yimg.com\/hr\/10374"}, posts); document.getElementById('delicious_shared_reading').innerHTML = myhtml; }; Delicious.Linkrolls_TL_SHARE = function(posts) { var myhtml = Delicious.Linkrolls.render({"user":"pongba","usertags":"toplanguage","version":"2","count":"15","sort":"date","extended":true,"icon":"s","name":true,"showadd":true,"BASE_URL":"http:\/\/delicious.com","STATIC_URL":"http:\/\/l.yimg.com\/hr\/10374"}, posts); document.getElementById('toplang_comments').innerHTML = myhtml; }; function DeliciousPrepare1(posts) { Delicious.posts = posts; } </script> <script type="text/javascript"> if (typeof window.Delicious == "undefined") window.Delicious = {}; Delicious.BLOGBADGE_DEFAULT_CLASS = 'delicious-blogbadge-line'; if (typeof(Delicious)=='undefined') Delicious = {}; if (typeof(Delicious.BlogBadge)=='undefined') { if (!Object.prototype.hasOwnProperty) { Object.prototype.hasOwnProperty = function(p) { return (typeof this[p] != 'undefined') && (this.constructor.prototype[p] !== this[p]); } } Delicious.BlogBadge = function() { return { DEBUG: false, url_root: Delicious.URL_ROOT || 'http://delicious.com', post_url_root: Delicious.POST_URL_ROOT || 'http://delicious.com/save', json_url_root: Delicious.JSON_URL_ROOT || 'http://feeds.delicious.com/v2/json/urlinfo', static_url_root: Delicious.STATIC_URL_ROOT || 'http://l.yimg.com/hr', default_class: "delicious-blogbadge-tall", posts_count: 5, did_init: false, badges: {}, log: function(msg) { if (this.DEBUG) if (window.console && console.log) console.log(msg); }, init: function() { if (arguments.callee.done) return; arguments.callee.done = true; if (Delicious.BLOGBADGE_DEFAULT_CLASS) { Delicious.BlogBadge.default_class = Delicious.BLOGBADGE_DEFAULT_CLASS; } this.badges = {}; // HACK: Packed version of md5.js is at the bottom of this file. // this.insertExtJSLibs(); this.insertBadgeCSS(); }, onload: function() { if (arguments.callee.done) return; arguments.callee.done = true; if(this.noAvailDiv)return; this.fetchBadgeJSON(); }, insertExtJSLibs: function() { this.forEach([ this.static_url_root+'/js/md5.js' ], function(u) { var scr = document.createElement("script"); scr.setAttribute("type", "text/javascript"); scr.setAttribute("src", u); document.getElementsByTagName("head").item(0).appendChild(scr); }); }, insertBadgeCSS: function() { var bcss = document.getElementById('delicious-blogbadge-css'); if (!bcss) { bcss = this.el('link', { 'id': 'delicious-blogbadge-css', 'type': 'text/css', 'rel': 'stylesheet', 'href': this.static_url_root+'/css/blogbadge.css' }, []); document.getElementsByTagName('head')[0].appendChild(bcss); } bcss = null; }, register: function(divid, url, title, opts) { var hash = hex_md5(url); var badge = { 'divid': divid, 'url': url, 'hash': hash, 'title':title, 'opts': opts||{} }; this.badges[hash] = badge; this.replaceChildNodes( document.getElementById(badge.divid), this.renderPostButton(badge) ); }, writeBadge: function(divid, url, title, opts, classname) { if (!classname) classname = this.default_class; var deliciousDivWrapper = document.createElement('div'); deliciousDivWrapper.innerHTML = '<div class="'+classname+'" id="'+divid+'"></div>'; var availElem = GetPositionalDiv(); if(!availElem) { this.noAvailDiv = true; return; } availElem.parentNode.insertBefore(document.createElement('br'), availElem); availElem.parentNode.insertBefore(deliciousDivWrapper, availElem); availElem.parentNode.insertBefore(document.createElement('br'), availElem); this.register(divid, url, title, opts); }, fetchBadgeJSON: function() { var json_url = this.json_url_root + "?callback=Delicious.BlogBadge.handleBadgeJSON" + "&noCacheIE="+(new Date()).getTime(); var showposts = false; for (hash in this.badges) { if (!this.badges.hasOwnProperty(hash)) continue; var badge = this.badges[hash]; json_url += "&hash="+encodeURIComponent(hash); if (!showposts && badge.opts.showposts) { showposts = true; json_url += "&showposts=yes&count="+this.posts_count; } } // TODO: Make work in XHTML pages with namespaces. var scr = document.createElement("script"); scr.setAttribute("type", "text/javascript"); scr.setAttribute("src", json_url); scr.setAttribute("id", 'delicious-blogbadge-json'); this.script_ele = scr; document.getElementsByTagName("head").item(0).appendChild(this.script_ele); }, handleBadgeJSON: function(data) { this.log(data); this.log(this.badges); for (var i=0,rec; rec=data[i]; i++) { var badge = this.badges[rec.hash]; if (badge && !badge.loaded) { this.buildBadge(badge, rec); badge.loaded = true; } } for (hash in this.badges) { if (!this.badges.hasOwnProperty(hash)) continue; var badge = this.badges[hash]; if (badge.loaded) continue; this.buildBadge(badge, { url: badge.url, total_posts: 0, top_tags: [] }); } document.getElementsByTagName("head").item(0).removeChild(this.script_ele); this.script_ele = null; }, buildBadge: function(badge, data) { badge.data = data; this.replaceChildNodes( document.getElementById(badge.divid), this.renderBadge(badge) ); }, buildPostURL: function(badge) { return this.post_url_root + '?url='+encodeURIComponent(badge.url)+ '&title='+encodeURIComponent(badge.title || '')+ '&jump=yes&partner=delbg'; }, renderPostButton: function(badge) { var el = this.bind(this.el); var post_url = this.buildPostURL(badge); return [ el('a', {'class':'save-to-link', 'href':post_url, 'title':'Bookmark '+badge.title+' on Delicious'}, [ el('span', {'class':'save-to-link-label'}, ['bookmark this on Delicious']) ]) ]; }, renderBadge: function(badge) { var el = this.bind(this.el); var map = this.bind(this.map); var data = badge.data; var total_posts = data.total_posts || 0; var top_tags = []; for (t in data.top_tags) { if (!data.top_tags.hasOwnProperty(t)) continue; top_tags.push(t); } top_tags.sort(function(a,b) { return ( data.top_tags[b] - data.top_tags[a] ); }); var posts = []; if (badge.opts.showposts) { for (var i=0, post; (i<this.posts_count) && (post=badge.data.posts[i]); i++) { posts.push(post); } } var post_bookmark_url = this.buildPostURL(badge); return [ this.renderPostButton(badge), (total_posts > 0) ? el('a', {'class':'url-link', 'href':this.url_root+'/url/'+data.hash}, [ el('span', {'class':'post-count-label-before'}, ['saved by ']), el('span', {'class':'post-count'}, [ total_posts ]), el('span', {'class':'post-count-label-after'}, (total_posts>1) ? [' other people'] : [' other person'] ), ]) : [ el('a', {'class':'empty-save-to-link', 'href':post_bookmark_url}, [ el('span', {'class':'empty-save-to-link-label'}, ['save this']), ]), el('span', {'class':'empty-message'}, [ 'be the first to bookmark this page!' ]) ], (! (total_posts > 0 && top_tags.length > 0) ) ? '' : [ el('div', {'class':'top-tags-container'}, [ el('span', {'class':'top-tags-title'}, ['tags: ']), el('ul', {'class':'top-tags'}, map(function(t) { return [ el('li', {}, [ el('a', {'href':this.url_root+'/tag/'+t, 'title':t}, [t]) ]), ' ' ]; }, top_tags) ) ]) ], (! (badge.opts.showposts && posts.length) ) ? '' : [ el('span', {'class':'latest-posts'}, [ el('span', {'class':'latest-posts-label'}, ['recent bookmarks: ']), el('ul', {}, map( function(post) { return el('li', {'class':'xfolkentry'}, [ el('a', {'class':'taggedlink', 'href':post.u}, [post.n]), (!post.d) ? '' : el('blockquote', {'class':'description'}, [post.d]), el('div', {'class':'meta'}, [ el('span', {'class':'meta-label-by'}, [' by ']), el('a', {'class':'author', 'href':this.url_root+'/'+post.un}, [post.un]), (!post.t.length) ? '' : [ el('span', {'class':'meta-label-to'}, [' to ']), el('ul', {'class':'tags'}, map(function(t) { return [ el('li', {'class':'tag'}, [ el('a', {'rel':'tag', 'href':this.url_root+'/'+post.un+'/tag/'+t}, [t]) ]), ' ']; }, post.t)) ], el('span', {'class':'meta-label-at'}, [' at ']), el('abbr', {'class':'created','title':post.dt+'Z'}, [post.dt+'Z']), ]) ]); }, posts)) ]), ], ' ', el('br',{},[]) ]; }, bind: function(func) { var obj = this; return function() { return func.apply(obj, arguments) }; }, forEach: function(list, fn) { fn = this.bind(fn); for (var i=0; i<list.length; i++) fn(list[i]); }, filter: function(fn, list) { var rv = []; fn = this.bind(fn); for (var i=0; i<list.length; i++) if (fn(list[i])) rv[rv.length] = list[i]; return rv; }, map: function(fn, list) { var rv = []; fn = this.bind(fn); for (var i=0; i<list.length; i++) rv[rv.length] = fn(list[i]); return rv; }, replaceChildNodes: function(parent, nodes) { while (parent.firstChild) parent.removeChild(parent.firstChild); return this.appendChildNodes(parent, nodes); }, appendChildNodes: function(parent, nodes) { if (!nodes || !nodes.length) return; for (var i=0; i<nodes.length; i++) { var node = nodes[i]; if (!node) continue; if (node.nodeType) parent.appendChild(node); else if ( (typeof(node) == 'object') && node.length) this.appendChildNodes(parent, node); else parent.appendChild(document.createTextNode(''+node)); } }, el: function(name, attrs, nodes) { var elem = document.createElement(name); if (attrs) for (k in attrs) { if (!attrs.hasOwnProperty(k)) continue; var v = attrs[k]; if (k.substring(0, 2) == "on") { if (typeof(v) == "string") { v = new Function(v); } elem[k] = v; } else { elem.setAttribute(k, v); } switch(k) { // MSIE seems to want this. case 'class': elem.className = v; break; } } if (nodes) this.appendChildNodes(elem, nodes); return elem; }, EOF:null }; }(); } // HACK: Packed version of http://pajhome.org.uk/crypt/md6/md5.js eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('e 1i=0;e 1g="";e p=8;f 1f(s){g K(A(D(s),s.o*p))}f 1w(s){g S(A(D(s),s.o*p))}f 1N(s){g L(A(D(s),s.o*p))}f 2b(w,v){g K(I(w,v))}f 2a(w,v){g S(I(w,v))}f 2c(w,v){g L(I(w,v))}f 2i(){g 1f("1R")=="1O"}f A(x,G){x[G>>5]|=1U<<((G)%E);x[(((G+1V)>>>9)<<4)+14]=G;e a=24;e b=-1Y;e c=-1X;e d=2h;z(e i=0;i<x.o;i+=16){e Y=a;e W=b;e X=c;e 1b=d;a=l(a,b,c,d,x[i+0],7,-2d);d=l(d,a,b,c,x[i+1],12,-28);c=l(c,d,a,b,x[i+2],17,29);b=l(b,c,d,a,x[i+3],22,-1T);a=l(a,b,c,d,x[i+4],7,-1Z);d=l(d,a,b,c,x[i+5],12,2j);c=l(c,d,a,b,x[i+6],17,-1P);b=l(b,c,d,a,x[i+7],22,-1Q);a=l(a,b,c,d,x[i+8],7,1S);d=l(d,a,b,c,x[i+9],12,-25);c=l(c,d,a,b,x[i+10],17,-26);b=l(b,c,d,a,x[i+11],22,-2f);a=l(a,b,c,d,x[i+12],7,2e);d=l(d,a,b,c,x[i+13],12,-2g);c=l(c,d,a,b,x[i+14],17,-27);b=l(b,c,d,a,x[i+15],22,1M);a=h(a,b,c,d,x[i+1],5,-1t);d=h(d,a,b,c,x[i+6],9,-1s);c=h(c,d,a,b,x[i+11],14,1u);b=h(b,c,d,a,x[i+0],20,-1v);a=h(a,b,c,d,x[i+5],5,-1r);d=h(d,a,b,c,x[i+10],9,1q);c=h(c,d,a,b,x[i+15],14,-1l);b=h(b,c,d,a,x[i+4],20,-1k);a=h(a,b,c,d,x[i+9],5,1m);d=h(d,a,b,c,x[i+14],9,-1n);c=h(c,d,a,b,x[i+3],14,-1p);b=h(b,c,d,a,x[i+8],20,1o);a=h(a,b,c,d,x[i+13],5,-1x);d=h(d,a,b,c,x[i+2],9,-1y);c=h(c,d,a,b,x[i+7],14,1I);b=h(b,c,d,a,x[i+12],20,-1H);a=k(a,b,c,d,x[i+5],4,-1J);d=k(d,a,b,c,x[i+8],11,-1K);c=k(c,d,a,b,x[i+11],16,1L);b=k(b,c,d,a,x[i+14],23,-1G);a=k(a,b,c,d,x[i+1],4,-1F);d=k(d,a,b,c,x[i+4],11,1A);c=k(c,d,a,b,x[i+7],16,-1z);b=k(b,c,d,a,x[i+10],23,-1B);a=k(a,b,c,d,x[i+13],4,1C);d=k(d,a,b,c,x[i+0],11,-1E);c=k(c,d,a,b,x[i+3],16,-1D);b=k(b,c,d,a,x[i+6],23,1W);a=k(a,b,c,d,x[i+9],4,-2z);d=k(d,a,b,c,x[i+12],11,-2F);c=k(c,d,a,b,x[i+15],16,2G);b=k(b,c,d,a,x[i+2],23,-2D);a=m(a,b,c,d,x[i+0],6,-2B);d=m(d,a,b,c,x[i+7],10,2I);c=m(c,d,a,b,x[i+14],15,-2O);b=m(b,c,d,a,x[i+5],21,-2M);a=m(a,b,c,d,x[i+12],6,2J);d=m(d,a,b,c,x[i+3],10,-2H);c=m(c,d,a,b,x[i+10],15,-2A);b=m(b,c,d,a,x[i+1],21,-2p);a=m(a,b,c,d,x[i+8],6,2q);d=m(d,a,b,c,x[i+15],10,-2o);c=m(c,d,a,b,x[i+6],15,-2n);b=m(b,c,d,a,x[i+13],21,2m);a=m(a,b,c,d,x[i+4],6,-2r);d=m(d,a,b,c,x[i+11],10,-2k);c=m(c,d,a,b,x[i+2],15,2y);b=m(b,c,d,a,x[i+9],21,-2t);a=u(a,Y);b=u(b,W);c=u(c,X);d=u(d,1b)}g H(a,b,c,d)}f F(q,a,b,x,s,t){g u(Z(u(u(a,q),u(x,t)),s),b)}f l(a,b,c,d,x,s,t){g F((b&c)|((~b)&d),a,b,x,s,t)}f h(a,b,c,d,x,s,t){g F((b&d)|(c&(~d)),a,b,x,s,t)}f k(a,b,c,d,x,s,t){g F(b^c^d,a,b,x,s,t)}f m(a,b,c,d,x,s,t){g F(c^(b|(~d)),a,b,x,s,t)}f I(w,v){e C=D(w);1d(C.o>16)C=A(C,w.o*p);e P=H(16),V=H(16);z(e i=0;i<16;i++){P[i]=C[i]^2L;V[i]=C[i]^2N}e 1c=A(P.18(D(v)),19+v.o*p);g A(V.18(1c),19+2C)}f u(x,y){e O=(x&N)+(y&N);e 1a=(x>>16)+(y>>16)+(O>>16);g(1a<<16)|(O&N)}f Z(T,M){g(T<<M)|(T>>>(E-M))}f D(n){e B=H();e J=(1<<p)-1;z(e i=0;i<n.o*p;i+=p)B[i>>5]|=(n.2l(i/p)&J)<<(i%E);g B}f L(B){e n="";e J=(1<<p)-1;z(e i=0;i<B.o*E;i+=p)n+=2s.2x((B[i>>5]>>>(i%E))&J);g n}f K(r){e U=1i?"2w":"2v";e n="";z(e i=0;i<r.o*4;i++){n+=U.R((r[i>>2]>>((i%4)*8+4))&1e)+U.R((r[i>>2]>>((i%4)*8))&1e)}g n}f S(r){e 1h="2u+/";e n="";z(e i=0;i<r.o*4;i+=3){e 1j=(((r[i>>2]>>8*(i%4))&Q)<<16)|(((r[i+1>>2]>>8*((i+1)%4))&Q)<<8)|((r[i+2>>2]>>8*((i+2)%4))&Q);z(e j=0;j<4;j++){1d(i*8+j*6>r.o*E)n+=1g;2K n+=1h.R((1j>>6*(3-j))&2E)}}g n}',62,175,'||||||||||||||var|function|return|md5_gg|||md5_hh|md5_ff|md5_ii|str|length|chrsz||binarray|||safe_add|data|key|||for|core_md5|bin|bkey|str2binl|32|md5_cmn|len|Array|core_hmac_md5|mask|binl2hex|binl2str|cnt|0xFFFF|lsw|ipad|0xFF|charAt|binl2b64|num|hex_tab|opad|oldb|oldc|olda|bit_rol|||||||||concat|512|msw|oldd|hash|if|0xF|hex_md5|b64pad|tab|hexcase|triplet|405537848|660478335|568446438|1019803690|1163531501|187363961|38016083|701558691|1069501632|165796510|643717713|373897302|b64_md5|1444681467|51403784|155497632|1272893353|1094730640|681279174|722521979|358537222|1530992060|35309556|1926607734|1735328473|378558|2022574463|1839030562|1236535329|str_md5|900150983cd24fb0d6963f7d28e17f72|1473231341|45705983|abc|1770035416|1044525330|0x80|64|76029189|1732584194|271733879|176418897|||||1732584193|1958414417|42063|1502002290|389564586|606105819|b64_hmac_md5|hex_hmac_md5|str_hmac_md5|680876936|1804603682|1990404162|40341101|271733878|md5_vm_test|1200080426|1120210379|charCodeAt|1309151649|1560198380|30611744|2054922799|1873313359|145523070|String|343485551|ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789|0123456789abcdef|0123456789ABCDEF|fromCharCode|718787259|640364487|1051523|198630844|128|995338651|0x3F|421815835|530742520|1894986606|1126891415|1700485571|else|0x36363636|57434055|0x5C5C5C5C|1416354905'.split('|'),0,{})) </script> <script type="text/javascript"> runOnLoad(function() { var note1 = document.createElement('div'); note1.innerHTML = '<div style="background:#000000;color:#ffffff;text-align:center;padding:10px 0px;">本博客已经迁移至 http://mindhacks.cn ,此处保留作为镜像,但不保证一定同步更新所有内容。原订阅 http://blog.csdn.net/pongba/rss.aspx (原始 Feed) 的朋友请转为订阅永久 feed : http://mindhacks.cn/feed/</div>'; var ct = GetPositionalDiv(); if(ct){ ct.parentNode.insertBefore(document.createElement('br'), ct); ct.parentNode.insertBefore(note1, ct); ct.parentNode.insertBefore(document.createElement('br'), ct); } }); </script> <script type="text/javascript"> Delicious.BlogBadge.init(); runOnLoad(function() { Delicious.BlogBadge.writeBadge("delicious-blogbadge-1", location.href, document.title, {}); Delicious.BlogBadge.onload(); }); </script> <script type="text/javascript"> runOnLoad(function( ){ function newScript( src ) { var script = document.createElement("script"); script.setAttribute("type", "text/javascript"); script.setAttribute("src", src); return script; } document.body.appendChild(newScript("http://feeds.delicious.com/v2/json/pongbablog?count=100&callback=DeliciousPrepare1")); document.body.appendChild(newScript("http://feeds.delicious.com/v2/json/tags/pongbablog?count=100&callback=doAllWorks")); document.body.appendChild(newScript("http://twitter.com/statuses/user_timeline/pongba.json?callback=twitterCallback2&count=8")); document.body.appendChild(newScript("http://feeds.delicious.com/v2/json/pongba/shared-reading?count=15&callback=Delicious.Linkrolls_CB_51902")); document.body.appendChild(newScript("http://feeds.delicious.com/v2/json/pongba/toplanguage?count=15&callback=Delicious.Linkrolls_TL_SHARE")); }); </script>
文章分类
C++
Andrei Alexandrescu
Andrew Lumsdaine
Bjarne Stroustrup
boost
C++ Standard Commitee
Doug Gregor
Hans J. Boehm
Jaakko Jarvi
Jeremy G. Siek
Matthew Wilson
newsgroups
boost.Developer
boost.User
comp.lang.c++.moderated
comp.std.c++
TopLanguage
Open Source
Ant
codeplex
Danga
Google AJAX Search API
Google Code Prettify
Google Web Toolkit
Hadoop
MS shared source initiative
notepad++
STLSoft
不认识的朋友们
Delphij
fatalerror99
flow with the life
Glacier
jimaxsoft
lifesinger@淘宝UED
Mr. 6
realazy
Robbin
SpiritEpic
TK
wuyizi
Yelz
丁丁虫
付翀
冰云
刘慈欣
卢昌海
吴欣安(atppp)
周爱民
和菜头
姬十三
守望轩
小花@BlogBus
林达华
浦宇平
白鸦
程化
罗浩|Startup Game
阮一峰
霍炬
飞之鸿
高远
鲍盛
机器学习/数据挖掘/信息检索/自然语言处理/认知科学/人工智能
AAAI
Apex
arXiv
Charles Kemp
Christopher Bishop
Christopher Manning
Cognitive Daily
Dan Jurafsky
David MacKay
ECML PKDD
Geoffrey Hinton
Herbert Simon
ICML
IJCAI
Jeff Hawkins
Jiawei Han
JMLR
Josh Tenenbaum
Larry Wasserman
Lucene
Marvin Minsky
MIT AI Lab
MIT Computational Cognitive Science Group
Mitchell Marcus
ML
NetLab
NIPS
Peter Norvig
Stanford AI Lab
Stanford NLP Lab
Stephen Boyd
Tom Mitchell
Trends in Cognitive Science
Vladimir Vapnik
Weka
Zhihua Zhou
技术
Coding Horror
High Scalability
Reddit
Stack Overflow
Steve Yegge
代码发芽网
淘宝UED团队
淘宝数据仓库团队
玩聚网
移山之道
其它
Gigapedia
Scientific American
Scientific American Mind
科学松鼠会
科幻世界
认识的朋友们
alai
chenyufei
dd
DreamHead
Googol
Jawley
Joyfire
littlestone
lxwde
Matrix67
realfun
RiceBall
roofalison
soloist
Tinyfool
windstorm
YongSun
书剑
云风
余晟
元凯宁
冯大辉(Fenng)
刘新宇
刘江@图灵
史晓明
吴新雨
周星星
周筠@博文视点
孟岩
张志强|阅微堂
张振
徐宥|4G Spaces
方舟@博文视点
曾登高
李笑来|Pure Pleasure
杨军
杨文博
熊节
王信文
王康生
苏杰@阿里巴巴
范怀宇
荣耀
莫华枫
蒋涛
袁泳(g9)|负暄琐话
许式伟
谢东升
谷文栋|Beyond Search
邹欣@MSRA
郑昀
阿朱
陈冀康@华章
陈怀兴
鲍志云
存档

原创  《C++0x漫谈》系列之:Concept, Concept! 收藏

C++0x漫谈》系列之:Concept, Concept!

 

By 刘未鹏(pongba)

C++的罗浮宫(http://blog.csdn.net/pongba)

 

 

C++0x漫谈》系列导言

 

这个系列其实早就想写了,断断续续关注C++0x也大约有两年余了,其间看着各个重要proposals一路review过来:rvalue-referencesconceptsmemory-modelvariadic-templatestemplate-aliasesauto/decltypeGCinitializer-lists…

 

总的来说C++09C++98相比的变化是极其重大的。这个变化体现在三个方面,一个是形式上的变化,即在编码形式层面的支持,也就是对应我们所谓的编程范式(paradigm)C++09不会引入新的编程范式,但在对泛型编程(GP)这个范式的支持上会得到质的提高:conceptsvariadic-templatesauto/decltypetemplate-aliasesinitializer-lists皆属于这类特性。另一个是内在的变化,即并非代码组织表达方面的,memory-modelGC属于这一类。最后一个是既有形式又有内在的,r-value references属于这类。

 

这个系列如果能够写下去,会陆续将C++09的新特性介绍出来。鉴于已经有许多牛人写了很多很好的tutor这里这里,还有C++标准主页上的一些introductiveproposals,如这里,此外C++社群中老当益壮的Lawrence Crowl也在google做了非常漂亮的talk)。所以我就不作重复劳动了:),我会尽量从一个宏观的层面,如特性引入的动机,特性引入过程中经历的修改,特性本身的最具代表性的使用场景,特性对编程范式的影响等方面进行介绍。至于细节,大家可以见每篇介绍末尾的延伸阅读。

 

Concept

 

好吧好吧,我承认我跳票了,上次说这次要写variadic templates的。但g9老大写了一篇精彩的散记,让我觉得concept应该先写,因为这实在是个有意思的特性,比variadic templates有意思多了。

 

我和Concept不得不说的事

事儿#1

看看下面这坨代码有什么问题:

 

std::list<int> li;

std::sort(li.begin(), li.end());

 

如果对人肉编译不在行的话,可以用你手头的编译器试一下。你会发现,你的编译器一碰到这简单而无辜的两行代码便会一反常态,跟个长舌妇似的吐出一大堆&#$@*^,令人牙酸的错误信息来。在使用C++模板库时这种编译错误井喷是家常便饭,动辄喷出令人应接不暇的4K字节的错误信息出来。你还以为不是编译器井喷,而是你自己RP井喷了,于是一脸无辜地跑去问模板达人,后者抬了抬眼皮,告诉你说“把list改成vector因为listiterator不是random的而std::sort需要randomiterator”,你一边在脑子里给这句话分词加标点符号一边想弄明白他是怎么从一堆毛线似的字符里抽象出这么个结论的。

 

实际上,这个问题比你想像得严重,其根本问题在于降低工作效率,你得在你本不需要花工夫的地方(人肉解析编译错误)花工夫;这个问题比你想像得普遍,乃至于居然有人把“能够独立地解决所有的编译与链接问题”也列在了“有实际开发工作经验”要求里面;这个问题比你想像得影响恶劣,因为你可以想像可怜的新手在两行貌似无辜的代码面前哭丧脸的模样——C++编译器就这样把一个可怜的潜在C++用户给扼杀了。你也可以想像为什么有那么多人不喜欢C++模板——其实语法只是其一个非主要的方面。

 

实际上你请教的那个达人并没有什么火星抽象能力,只不过是吃过的桥比你走过的盐还多而已。而这,还预示着另一个问题,就是能人肉解析模板编译错误居然也成为了衡量C++达人与否的一个标准不信你去各个坛子上转一转看看有多少帖子是询问关于编译错误的问题的,其中又有多少是关于模板编译错误的。

 

更小概率的是居然还存在一个专门解析STL相关错误信息的“STL错误解码器”——STLFilt。这玩意帮你把编译错误转换成人能识别的自然语言,不错是不错。可惜STLFilt有了,BoostFilt呢?ACEFilt呢?我自己写的模板库呢?

 

其实,造成这个问题的直接原因是C++的类型系统的抽象层次太低。C++的静态强(也有人说C++的类型系统其实是弱类型系统,anyway)类型系统所处的抽象层面是在基本类型(intdoublechar…)层面的。一方面,C++虽然拥有对自定义类型的上乘支持(比如,支持将自定义类型的接口装扮得跟内建类型几乎毫无二致——vector vs. build-in array),然而另一方面,C++的类型系统却对于像vector这样的抽象从语意上毫不知情。直接的后果就是,一个高层的类型错误往往以相差了十万八千里的底层类型错误表现出来,结果就是你得充当一次福尔摩斯,从底层错误一直往上回溯最终找到问题的发生点。譬如一开始给出的那个例子:std::sort(li.begin(), li.end());的错误,如果C++类型系统的抽象层能高一些的话(所谓抽象层次高,就是知道高层抽象概念(Concept)的存在,如“随机迭代器”这个概念),给出的错误无非就是:“list的迭代器不满足随机迭代器这个概念(concept)的要求(requirements)”。然而由于C++并不知道所谓concept的存在,所以问题到它眼里就变成了“找不到匹配的operator+…”一堆nonsense

 

事儿#2

大二上学期的时候我们上一门计算方法的课程,期末考试要写一些矩阵算法。地球上的程序员大抵都知道矩阵算法不用Matlab算基本等于没事找抽,一大堆accidental complexities在那恭候着,一个index错误能让你debug到抓狂。当时我C++用得半斤八两,模板七窍也差不多通了六窍;为了到上机考试的时候节省点时间,就事先写了一个简单的矩阵库,封装了一些基本的操作和像高斯消元这种基本算法。

 

那个时候你能指望我知道TDD?还是XP?或者STLLint?于是呢?写了一个简单的程序,简单使用了一下写好的库,发现编译通过后就兴冲冲地告诉哥们说:大家不用怕,有我这Matrix库罩着,写算法跟写伪码差不到哪去!

 

两天后上机考试,程序不同了,等于测试用例不同了,结果原来没有出现的编译错误一下统统跑出来了。原来为什么不出现?一个原因是原来有些成员函数就没用到,C++说,在一个模板类里面,没用到的成员函数是不予编译的。那不予编译就代表不予纠错吗?不予类型检查吗?令人悲伤的是,的确如此。或者把置信度提高一点说,几乎如此。为什么?看看下面的代码:

 

template<typename T>

void f(T& t)

{

t.m();

}

 

你说编译器看着这个函数,它怎么做类型检查?它怎么知道t上面有没有成员函数m?它连t的类型都不知道。“很久很久以前,模板就是这样破坏模块式错误检查的

 

实际上,C++98那会,为了能够尽早尽量检查模板代码中的隐患,以响应“防范胜于救灾,隐患重于明火”的号召,C++甚至将模板上下文中的代码中的名字生生分成了两类,一类叫dependent names,一类叫non-dependent names。举个例子,上面那段代码中的m成员函数就是dependent的,因为它的隐含this参数t的类型是dependent的;对于dependent name,不作类型检查——原因刚才讲过,因为类型信息根本就没有。剩下的就是non-dependent names了,比如:

 

void g(double); // #1

 

template<typename T>

void f()

{

g(1);

}

 

void g(int); // #2

 

int main()

{

f<int>();

}

 

这里f里面调用的g绑定到哪呢?答案是#1。因为g是个non-dependent name(虽然它位于模板函数(上下文)里面)。而对于non-dependent name,还是赶紧进行类型检查和名字绑定吧,有错误的话也能早点暴露出来,于是g便在它的使用点“g(1)”处被查找绑定了——尽管#2处的g(int)是一个更好的匹配,但在g(1)处只有g(double)是可见的,所以g(double)被编译器看中了,可怜的g(int)只能感叹“既生g(int),何生g(double)…”。

 

这,便是臭名昭著的腰斩…sorry…是二段式名字查找,C++著名的复杂性来源之一。说它臭名昭著还有一个原因——在众多编译器支持良莠不齐的C++复杂特性中,它基本可以说是位居第一(第二估计要留给友元声明了),VC挣扎到8.0还是没有实现二段式名字查找,而是把所有的工作留到模板实例化点上进行,结果就是上面的例子中会选中#2

 

D&E中对此亦有详细介绍。

 

实际上,这个二段式名字查找的种种问题正从一个侧面证明了早期类型检查是何等重要,动态语言的老大们在Ruby翻出来的旧瓶新酒Duck Typing吵翻了天其实说的也是这个问题(sorry,要加上“之一”)。

 

事儿#3

在一个无聊的午后,我在敲打一坨代码,这是一个算法,算法要用到一个容器,算法是用模板来实现的:

 

template<typename ContainerT>

void XXXAlgo(ContainerT cont)

{

… cont.

 

在我敲打出“cont”加点号“.”之后,我习惯性地心理期待着“智能”的IDE能够告诉我cont上面有哪些成员函数,正如我们每次敲打出“std::cout.”之后一样。习惯成自然,你能说我不对么?难道你金山糍粑用久了不也一样在读影印版纸书遇到不认识单词的时候想着把手指头伸过去指着那个单词等着跳出个词条窗口来?难道只是我?咳咳

 

问题是,我知道XXXAlgo的那个模板参数ContainerT是应当符合STLContainer概念(concept的,我当然希望编译器也能知道,从而根据Container概念所规定它必须具有的成员函数来给我一个成员函数列表提示(beginendsize…),难道这样的要求很过分吗?它没有道理很过分啊,觉得它很过分我会说的啊,不可能它明明不过分我偏要说它很过分,他很过分我偏要说它不过分啊你觉得这要求过分你就说嘛乱敲键盘是不好滴,键帽掉下来砸到花花草草也不好啊你看,“.”键又给你磨平了

 

一方面,程序员一脸无辜地认为IDE应该能够看到代码里面的ContainerT暗示着这是一个符合STLContainer概念的类型。而另一方面IDE厂商却也是理直气壮:写个ContainerT就了不起啊,万一遇到个C过来的,写成ContT我怎么办?写成CntnrT哪?是不是要我实现一个spell checker?再说你觉得ContainerT是对应STLContainer概念的,别人还用这个单词来对应线程池呢怎么办捏?什么?他不知道“poor”怎么写管我啥事嘞?我身为一个IDE,根据既有的信息,作出这样的假设,既合情,也合理

 

事儿#4(此事纯虚虚构,如有巧合,算你运气背)

一天,PM跑过来告诉你说:“嘿,猜怎么着,你写的那坨模板代码,隔壁部门人用了说很不错,希望你能把代码和文档完善一下,做成一个内部使用的库,给大家用,如何?”你心头一阵花枝乱颤:“靠!来部门这么久了,C++手段终于可以展露一下了。”于是废寝忘食地按照STL文档标准,遵照C++先贤们的教诲,写了一个漂漂亮亮的文档出来。里面Concept井井有条,Requirements一丝不苟

 

动态语言的老大们常挂在嘴边的话是什么?——需求总是在变的。又一天,你发现某个Concept需要revise了,比如原来的代码是这样的:

 

template<typename XXX>

void f(XXX a)

{

 

a.m1();

}

 

本来XXX所属的那个Concept只要求有m1成员函数。后来因需求变更,XXX上需要一个新的成员函数m2。于是你的代码变成了:

 

template<typename XXX>

void f(XXX a)

{

 

a.m1();

a.m2();

}

 

但仅改代码是不行的,文档里面关于XXX所属的那个Concept的描述也要同步修改可惜天色已晚,良宵苦短,你准备睡一觉明天再说结果第二天一早你就被boss叫去商量一个新的项目(因为你最近表现不错),于是你把这事给忘了。于是跟代码不一致的文档就留在那里了

 

这种文档和代码不一致的情况太常见了,根本原因是因为代码和文档是物理上分离的,代码不能说谎,因为要运行,但文档呢?什么东西能验证文档精确反映了代码呢?除了往往忽视文档的程序员们之外没有其他人。这个问题是如此广泛和严重以至于程序员们干脆就近乎鸵鸟式地倡导“代码就是文档”了,这句话与其说是一个陈述句,不如说是一个美好的愿景(远景?)。

 

好吧,好吧,你记性好,这点小事你不会忘掉,第二天一早你就把文档给改了,你真是劳模。可惜过一天,需求居然又改变了(你心说是哪个家伙负责客户需求分析的?!),这下你需要修改Concept继承体系了

 

你看,可能造成文档和代码脱节的因素太多了,一般一段时间以后,能说得上话的也就剩代码,文档只能拿来看看“系统应该是什么样子的”,只有代码才陈述了“系统实际是什么样子的”。

 

然而,如果文档就在代码当中呢?不,我不是说注释,你又不是不知道要写出合格的注释比写出合格的小说还要难。我是说,代码就是文档文档就是代码

 

此外,把Concept约束写在代码里面还有一个好处就是能够使得被调用函数和调用方之间的契约很明显,Concept的作用就像门神,告诉每一个来调用该函数的人:“你要进去的话必须满足以下条件”。RubyDuck Typing被诟病的原因之一就是它的Concept在代码里面是隐式的,取决于对象上的哪些方法被使用到了。

 

事儿#5

重构重不重要Martin Fowler叔叔笑了

 

原来我抽屉里有这么一段代码:

 

template<typename XXXConcept>

void foo(XXXConcept t)

{

t.m1(); // #1

}

 

template<typename XXXConcept>

void bar(XXXConcept t)

{

t.m1(); // #2

}

 

现在我想对代码作一种最简单的重构——改名。m1这个名字不好听,我想改成mem。于是我指望编译器能替我完成这个简单的任务,我把鼠标指到#1处,在m1上右击,然后重命名m1mem。同时很显然我期望“智能”的编译器能够帮我把#2处也改过来,因为它们用的是同一个concept上的成员函数。

 

但编译器不干,原因见事儿#3。或者见这篇blog,后者举了一个类似的例子——如果我们重命名实现了那个XXXConcept的类上的m1方法,那么#1#2处的调用能被自动重命名吗?Ruby Refactoring Browser的作者笑了

 

事儿#6

很久很久以前我写了一个容器类。这个容器类里面该有的功能都有了唯一的问题是,当时我还不知道STL(准确地说是就算知道也没用),结果呢?这个各方面功能都完备的容器类的使用界面(接口)并不符合STL容器的规范。比如我把begin()叫做start(),把end()叫做还是叫做end()(不然总不能叫finish()吧?)。我还把empty()叫做isEmpty()而另一方面我的empty()实际却做的是clear()的工作

 

后来,我又写了一个算法,这个算法是针对STL容器的,你问我干嘛不针对迭代器编程?很简单,因为我要用到emptyfront/backclear等成员函数。基于迭代器编写也不是不行,就是得再费一袋烟此外还有两个问题,一是效率,而是影响算法使用说到效率

 

现在,我想让我的这个算法也能操纵我原来那个古董容器(我不是指我家那个慈禧尿壶),但因为那个古董容器的接口跟我的算法要用到的接口不一致:

 

class MyCont { … bool isEmpty(); … };

 

template<typename Cont>

void f(Cont cont){ … cont.empty(); … }

 

怎么办?修改MyCont的实现?可以,因为这个MyCont是我写的,后者意味着两点:一,我有权修改它。二,我写的库没其他人用。可是如果MyCont是位于另一个库当中的呢?如果有一堆依赖于它的既有代码呢?

 

或者,写个wrap类?还是太麻烦了,况且wrap手法也不是没有自己的问题。我们只不过想适配一下接口而已。

 

其实,我们只想对编译器说一句:MyContisEmpty其实就是empty,您行行好就放行吧

 

事儿#7

函数重载重不重要?废话。多态重不重要?还是废话。

 

SFINAE重不重要?C++ Templatesgeeks们都笑了

 

C++里面,SFINAE技术已经成为GP的奠基技术之一(老大当然是sizeof技术)。boost里面为此专门引入了一个库,叫boost::enable_if。该库,如boost::mpl一样,被boost里面的众多子库依赖。比如boost::function库就用到了该技术

 

简而言之,SFINAE技术允许你根据类型的编译期信息实现多态行为:

 

template <class T>

typename enable_if<boost::is_arithmetic<T>, T>::type

foo(T t) { return t; }

 

如果T是算术类型,那么这个foo函数模板就能够实例化,否则就不会。

 

另一项相关的模板技术是Tag DispatchSTL中大量运用这种手法:

 

template<typename InputIter>

typename iterator_traits<InputIter>::difference_type

distance(InputIter it1, InputIter it2)

{

  return distance(it1, it2,

typename iterator_traits<InputIter>::iterator_category());

}

 

如果typename iterator_traits<InputIter>::iterator_categoryrandom_access_iterator_tag的话就会跳转到:

 

… distance(InputIter it1, InputIter it2 , random_access_iterator_tag)

{ … }

 

如果是bidirectional_iterator_tag的话就会跳到:

 

… distance(InputIter it1, InputIter it2 , bidirectional_iterator_tag)

{ … }

 

然而,这些与其说是技术(techniques),不如说是技巧(tricks)。它们的存在增加了C++GPaccidental complexity。它们,正如大多数的C++模板技巧一样,本不在C++设计的考虑之内,而是后来被人们发现出来的。当然你可以说C++是唯一一门语言之父本人需要别人教他怎么用的语言,这的确很奇妙,然而当程序员抓耳挠腮地对付这些技巧的时候,恐怕更多的是恼火。比如这个:

 

template <typename _Iterator>

struct iterator_traits {

  typedef typename _Iterator::difference_type difference_type;

};

 

template <typename _InputIterator>

inline typename iterator_traits<_InputIterator>::difference_type

distance(_InputIterator, _InputIterator);

 

double distance(const int&, const int&);

 

void f() {

  int i = 0;

  int j = 0;

  double d = distance(i, j);

}

 

符合SFINAE的条件吗?不符合吗?符合吗?

 

说到底,这些细节本不该由程序员来操心。我们想要的是一个简洁明了地表达我们想法的工具。

 

事儿#8

如果一个生物走起路来像个火星人,

说起话来像个火星人,

回起贴来像个火星人,

那他肯定就是火星人。

——火星人判别最高纲领

 

Ruby的串红使火星人类型系统焕发出了第二春。

 

Rubyers的口号是,我不关心你是不是真的是火星人,看你丫的回帖像刚从火星回来的,你一定就是火星人!

 

Sorry,用严肃一点的话来说,就是“不关心一个对象的具体类型,而只关心一个对象的行为”。用镐头书上的例子就是:

 

class Customer

def initialize(first_name, last_name)

@first_name = first_name

@last_name = last_name

end

def append_name_to_file(file)

file << @first_name << " " << @last_name

end

end

 

file << @first_name,这里file并不一定要是真正的文件,而只要是一个支持“<<”操作的对象即可(想起C++的流插入符了吗?)。所以要测试这个Customer类的append_name_to_file,也就不一定要真正创建一个文件出来传给它当参数,只要传一个支持<<操作的对象给它就可以了——比如一个String对象。用String对象的好处就是检查被写入到这个String里面的东西很容易,而用File对象的话还得开文件关文件的,麻烦。

 

事实证明Ruby的火星人类型系统是非常灵活的。镐头书上还举了另一个实际的例子:有这么一坨代码,它遇到数据量大的时候就变得奇慢,项目期限在即,当花了一点时间检查问题所在之后,发现问题在于代码中创建了许多的String临时对象,而速度问题则是因为GC运行起来了,之所以有这么多String临时对象是因为一个循环里面不断往一个String对象上面Append新子串,导致原来的串对象被丢弃,一地鸡毛。

 

结果还是火星人类型系统“to the rescue”。通过仅改变一两行非关键代码,该项目得救了。作出的改变就是把那个String对象换成一个Array,这样每次往上面Append新串的时候都会把这个新串当作一个新的元素挂到这个Array对象上,活像一串串腊肉;由于没有旧串被丢弃,因此也就不会出现遍地垃圾的情况。

 

好吧,我承认我在练习中学语老师教的欲抑先扬手法。不过Ruby Fans大可不必激动,因为两个原因:一,C++也有同样的问题,所有的模板代码用的本质上也都是火星人类型系统(C++98只支持完全unconstrained templates)——管你实际上是不是迭代器,只要你有++--*->等操作就行。二,火星人类型系统的危险是理论上的,实际上谁也没有案例证明它导致了什么灾难。比如在Cedricblog上这篇“The Perils of Duck Typing”后面就有人跟贴说一个JarFile上有一个explode(解压)和一个NuclearBomb上有一个explode(爆炸),于是你的算法把一个核弹给“解压”(爆炸)了。从这个例子的极端也不难看出其实这种危险的可能性并不大。有一次我在新闻组上发帖,扯到这个问题上,也有人举了一个例子,说手指有一个方法叫“插”(Plug),而插头也有一个方法叫“插”(Plug),于是不管三七二十一的算法就面临把一根手指(谁的谁倒霉)插到插座中去的危险。

 

这些例子说到底都有点飘逸,不切实际。具体的例子你问我我也没有,或许C++里面倒是可以捏造出一个“比较”实际一点的来:

 

template<typename StreamT>

void f(StreamT& stream) { stream << 1; }

 

int i;

f(i);

 

这段代码编译器也乐呵呵地编译了,因为整型是支持<<(位移)操作的。这里的错误很显然,但若是藏在成千上万代码当中,因为一个打字错误而漏进去的话,也许就不那么显然了。

 

从本质上说,火星人类型系统是将语法结构的同一性视为语意层面的同一性(即所谓的Structural Conformance);这才是它的根本问题。而另一方面,传统的接口继承(即所谓的Nominal Subtyping)则更严格:当你继承自一个接口的时候,你明确而清醒地知道你是要实现该接口所表达的抽象(语意),你不会“一不小心”(accidentally)实现了一个接口的,你必须写上“implements …Java/ public … C++)”几个大字母才行。

 

Concept to the rescue

话说到这份上如果你还不知道我要说什么那我就继续说吧

 

以上八大问题一直都是GP中被广为争论的问题,其中duck typing#8)在动态语言社群争论得比在C++里面还要激烈得多;同时它们也都是由来已久的问题,有的甚至久远到BjarneD&E中就已经遇见到了,只是当时C++标准化的进度太紧来不及解决而已,这一晃就是10

 

没错,它们全部都可以用Concept来漂亮地解决。或者换个说法,Concept的出现就是为了解决以上这些问题的——

 

#1(编译错误问题)——有了ConceptC++的类型系统抽象层次便提高了一个级别,在遇到编译错误的时候便能说“XXX不满足XXXConcept”这样的话了。

 

#2(模块式类型检查问题)——有了Concept,原本所谓的unconstrained templates便可以做成constrained。比如STLfor_each算法就变成了这样:

 

template<InputIter Iter, Callable1 Fun>

Fun for_each(Iter first, Iter last, Fun func)

{

  for(;first != last; ++first) func(*first);

  return func;

}

 

其中InputIteratorCallable1都是Concepts

 

auto concept Callable1<typename F, typename X> {

  typename result_type;

  result_type operator()(F&, X);

};

 

concept InputIterator<typename X> : EqualityComparable<X>, …

{

 

  pointer operator->(X);

  X& operator++(X&);

  postincrement_result operator++(X&, int);

  reference operator*(X);

}

 

有了这些Concept,编译器在对for_each作类型检查的时候便能够往InputIterator/Callable1里面进行名字查找:“first != last”可不可行?只要看first的类型支不支持“!=”,那first的类型支不支持“!=”呢?因为first的类型Iter是满足concept InputIterator的,那就只要看InputIterator里面有没有“!=”就行了,有吗?没有?哦,不好意思,忘记说了,InputIterator是继承自EqualityComparable的,后者里面定义了“!=”。

 

auto concept EqualityComparable<typename T, typename U = T> {

bool operator==(T a, U b);

bool operator!=(T a, U b) { return !(a == b); }

}

 

同样的,“++first”这个表达式可行吗?只要看first的类型支不支持“++”就可以了,后者只要看InputIterator这个concept支不支持“++”,答案是支持。

 

#3IDE智能提示问题)——编译器既然知道了concept的存在,当你敲下iterctrl+空格的时候编译器便能够通过解析InputIterator这个concept的定义来告诉你iter对象支持哪些操作了。

 

#4(文档代码分离问题)——瞧一瞧for_each的声明,原来(C++98)是:

 

template<typename InputIterator … >

void for_each(InputIterator iter …);

 

现在(C++09)是

 

concept InputIterator

{

}

 

template<InputIterator Iter … >

void for_each(Iter …);

 

区别在什么地方?原来的代码中没有concept InputIterator这样的声明,你看着InputIterator这么个单词,得去STL的文档里面查才知道它到底有那些requirements。有了concept之后呢?只要翻开InputIterator这个concept的定义就看到了,后者将位于C++09<iterator>头文件中。

 

#5(重构问题)——重构?当然!有了concept,要重构的时候只要修改concept定义,所有使用了该concept内的函数的地方都可以容器地作出改变。

 

#6(接口适配问题)——实际上前面“事儿#6里面提到的古董容器+非古董算法的例子虽然能说明问题,但总是不够巧妙。还不如直接抄Concept六君子OOPSLA ‘06上的牛paper中的例子,Douglaspaper里面举了一个图论库的例子:有一个矩阵算法,但另外还有一个图(Graph),就算没吃过图总见过图走路吧——图是可以用矩阵来表示的,所以只要用concept_map把图类的接口适配一下就可以拿那个现成的矩阵算法来操纵了,如下:

 

template<Graph G>

concept_map Matrix<G>

{

  typedef int value_type;

  int rows(const G& g) { return num_vertices(g); }

  int columns(const G& g) { return num_vertices(g); }

  double operator()(const G& g, int i, int j)

  {

    if (edge_e = find_edge(ith_vertex(i, g), ith_vertex(j, g), g))

      return 1;

    else return 0;

  }

};

 

#7SFINAEtraitstag dispatching等等)——有了concept,前文的代码就可以这么写:

 

template<Arithmetic T>

T foo(…) { … }

 

只有对于符合Arithmetic这个concept的类型T,该函数才“存在”。

 

concept还给函数重载带来了更强大的能力,比如:

 

concept Initializable<typename T>

{

  void initialize();

}

 

template<Initializable T>

void foo(…) { … }

 

template<typename T>

void foo(…) { … }

 

为什么说它更强大?不信你在C++98下实现看看。

 

#8(火星人类型系统问题)——火星人的危险性前文已经阐述了。其危险在于将语法结构同一性视为语意同一性。用传统的接口继承就没有这个问题,因为当你继承自一个接口的时候,你明确知道你在干嘛——实现这个接口的语意。因此,在C++09concept中,缺省的concept是“非auto”的,也就是说:

 

concept Drawable<typename T>

{

  void T::draw() const;

}

 

class MyClass

{

  void draw() const;

};

 

这种情况下MyClass是不会自动满足Drawable这个concept的(尽管它的确实现了一个一模一样的draw()函数),这是为了避免无意间实现了一个不该实现的concept。要想让MyClass实现Drawable这个concept,必须显式地说明这一点:

 

concept_map Drawable<MyClass> { }

 

是不是看上去很像模板特化?实际上concept的内部编译器实现正是利用既有的一套模板特化系统来进行的。

 

但是如果每个concept都要靠concept_map来支持的话太麻烦,有些基本的concept比如EqualityComparable——只要一个类型重载了operator==,那么就肯定是EqualityComparable的。这个论断几乎肯定是安全的,因为没有谁会不知道operator==的语意吧?所以,EqualityComparableauto的:

 

auto concept EqualityComparable<typename T, typename U = T> {

bool operator==(T a, U b);

bool operator!=(T a, U b) { return !(a == b); }

}

 

这样一来如果你的类实现了operator ==,你不需要将它concept_mapEqualityComparable,就能自动(auto)实现EqualityComparable;一句话,回到原始的“结构一致性”上面。

 

关于auto的另一个作用,我想到了一个绝妙的介绍,但这里空白太小写不下了,请听下回分解:-)

 

延伸阅读

[1] Concepts: Linguistic Support for Generic Programming in C++

此篇是Concepts的权威饲养指南,高屋建瓴巨细靡遗地介绍了Concept的方方面面。

[2] An Extended Comparative Study of Language Supports for Generic Programming

此篇对各种语言对GP的支持做了极其详尽的survey,其中也提到了concept的一些东西,很有价值的一篇paper

[3] Concept checking – A more abstract complement to type checking

当年,C++的老豆率先发难,写了这篇最早的concept paper,其间对三大实现策略作了高屋建瓴的比较,对掌握concept的本质有非常好的帮助。

[4] Concepts

一番刀光剑影你来我往之后,user-pattern派(由Bjarne本人发起)和function signature派(由Douglas带领)终于联合起来;这是第一篇署名Bjarne Stroustrup & Douglas GregorConcept ProposalFunction Signature的做法被正式确定下来(主要原因之一是它提供了#6(类型适配)这个大大的好处)。

[5] Proposed Wording for Concepts(rev#1)

这个就不用说了,截止到最近的concepts标准提案。

[6] Concepts for the C++0x Standard Library Utilities(rev#2)

这个自然也不必说了,C++0x标准库里面的一些基本的concepts定义。

[7] http://www.generic-programming.com

ConceptGCC的官方站,含GCC实现的下载,以及历届concepts相关paper

[8] Yet Another Haskell Tutorial

其实conceptHaskell里面早就实现了,不过名字不叫concept,而叫type class。看一看haskell的实现对理解C++09 Concepts肯定也是有帮助的。

 

目录(展开C++0x漫谈》系列文章)

 

发表于 @ 2007年08月04日 14:26:00 | 评论( loading... ) | 编辑| 举报| 收藏

旧一篇:Generic Programming - What are you, anyway? | 新一篇:争论C++前你应当知道什么(rev#1)

  • 发表评论
  • 评论内容:
  •  
Copyright © pongba
Powered by CSDN Blog