<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Piano &#38; Programming</title>
	<atom:link href="http://elapsed.yo2.cn/feed/" rel="self" type="application/rss+xml" />
	<link>http://elapsed.yo2.cn</link>
	<description>专业程序员，业余钢琴手。</description>
	<lastBuildDate>Wed, 21 Mar 2012 08:44:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>少写代码就是好代码</title>
		<link>http://elapsed.yo2.cn/2012/77120/no-code-is-good-code/</link>
		<comments>http://elapsed.yo2.cn/2012/77120/no-code-is-good-code/#comments</comments>
		<pubDate>Tue, 10 Jan 2012 08:23:12 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77120</guid>
		<description><![CDATA[一种理想的语言，针对一个问题应该提供尽可能少的实现方式，这样，即使用户只掌握了这门语言的大概，也可避免误用。但是反过来说，这种语言是不存在的。Python在代码格式上算是做到了这一点，但是以严格的缩进代替明确的首尾标记，自动格式化也就不太可能了，这种缩进一旦丢失也很难恢复。很多语言刚发明时，都力求简洁，没有太多花哨的语法和功能，但是一路走来，复杂的语法和功能越加越多，做一件事就不止一种两种甚至三种方式了。哪一种情况下用哪一种方式，就得看程序员的素质。]]></description>
			<content:encoded><![CDATA[<p>一种理想的语言，针对一个问题应该提供尽可能少的实现方式，这样，即使用户只掌握了这门语言的大概，也可避免误用。但是反过来说，这种语言是不存在的。Python在代码格式上算是做到了这一点，但是以严格的缩进代替明确的首尾标记，自动格式化也就不太可能了，这种缩进一旦丢失也很难恢复。很多语言刚发明时，都力求简洁，没有太多花哨的语法和功能，但是一路走来，复杂的语法和功能越加越多，做一件事就不止一种两种甚至三种方式了。哪一种情况下用哪一种方式，就得看程序员的素质。</p>
<p>我常常看到项目中，有像下面这样的代码：</p>
<pre>
bool bVisible = (FLAG_SHOW == strValue) ? true : false;
</pre>
<p>其实，这算是好的情况了，如果你跟他说这个代码可以写得更简单些：</p>
<pre>
bool bVisible = (FLAG_SHOW == strValue);
</pre>
<p>他可能会跟你争辩，说加上true/false的写法更容易理解。这就难说了，因为是否容易理解是个仁者见仁智者见智的问题，说不定还有审美趣味的因素，而我真的没有更好的理由说服他，毕竟，这只是个微不足道的单行代码而已。</p>
<p>下面的if...else语句与此类似，每次看到它我都要强忍住随手改之的冲动。</p>
<pre>
if (&lt;bool表达式&gt;) {
  return true;
} else {
  return false;
}
</pre>
<p>少写代码意味着不写不必要的代码。避免坏代码最好的方法就是少写代码。少写代码往往比多写代码需要更多的经验和觉悟。比如让你对一组浮点数进行排序：</p>
<pre>
void sort(float* values, size_t size);
</pre>
<p>最简单的方法就是使用STL的sort()：</p>
<pre>
void sort(float* values, size_t size) {
  std::sort(values, values + size);
}
</pre>
<p>这样的代码再好不过了。前提是你首先得了解STL（经验），其次要能抑制住重新发明轮子的冲动（觉悟）。</p>
<p>看一个我们项目中的例子，下面这个函数替换子字符串：</p>
<pre>
std::string replace(const std::string &amp; src,const std::string &amp; oldstr,const std::string &amp; newstr)
{
  size_t pos = 0;
  std::string buffer = src;
  std::string::size_type newlen = newstr.length();
  std::string::size_type oldlen = oldstr.length();
  while(true)
  {
    pos = buffer.find(oldstr, pos);
    if (pos == std::string::npos) break;
    buffer.replace(pos, oldlen, newstr);
    pos += newlen;
  }
  return(buffer);
}
</pre>
<p>我之所注意到这个函数，是因为最近一位同事在重构时对它进行了优化，重构前它是下面这个样子：</p>
<pre>
std::string str::replace(const std::string &amp; src,const std::string &amp; oldstr,const std::string &amp; newstr)
{
  std::string buffer=src;
  const std::string::size_type oldlen=oldstr.length();
  if (oldlen&gt;0)
  {
    std::string::size_type index=0;
    std::string::size_type newlen=newstr.length();
    if (newlen)
    {
      if (newlen&lt;oldlen)
      {
        if ((index=newstr.find(oldstr))!= std::string::npos)
        {
          newlen=index;
        }
      }
    }
    while ((index=buffer.find(oldstr,index))!= std::string::npos)
    {
      buffer.replace(index,oldlen,newstr);
      index+=newlen;
    }
  }
  return(buffer);
}
</pre>
<p>我想说的是，这个重构做得很不到位。其实对于字符串替换这种问题，肯定有人早就做过了，并且做得很好，比如boost algorithm就提供了replace_all/replace_all_copy。所以，这个函数可以直接删掉，用boost好了。</p>
<p>在我们项目中，重新发明轮子的事情实在是太多了，比如下面这个从字符串转浮点数的函数（应该是从网上拷贝来的）：</p>
<pre>
static double string_to_double(const char* s)
{
    double val = 0,power = 1;
    int i,sign = 1;
    for(i=0;isspace(s[i]);i++);
    sign=(s[i]=='-')?-1:1;
    if(s[i]=='+'||s[i]=='-')
        i++;
    for(val=0.0;isdigit(s[i]);i++)
        val=10.0*val+(s[i]-'0');
    if(s[i]=='.')
        i++;
    for(power=1.0;isdigit(s[i]);i++)
    {
        val=10.0*val+(s[i]-'0');
        power*=10.0;
    }
    return sign*val/power;
}
</pre>
<p>当然，还有double_to_string，我都不好意思贴出来了。</p>
<p>少写代码不是说可以随便拷贝别人的代码，特别是从网上。再举一个我们项目中的一个例子（靠！你有没有发现我们项目的伟大！？），读取一个文本文件，把内容存在字符串里：</p>
<pre>
bool ReadFile(const std::string&amp; file, std::string&amp; stream)
{
    /*
     * C version -- read file
     */
    // Read file characters line by line(perfect)
    FILE* fpin = NULL;
    const int   maxLen = 180; // Assume the character number of a line is less than 180
    char  buf[maxLen];
    fpin = fopen(file.c_str(), "r");
    if (NULL == fpin) return false;
    while (!feof(fpin) &amp;&amp; fgets (buf , maxLen , fpin)) // Judge the end of file
    {
        stream += buf;
    }
    fclose(fpin);
    return true;
}
</pre>
<p>这段代码是从网上拷的，连注释都保留原样。"假定每行字符数少于180"这一句特别显眼，这种假设是荒谬的。</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2012/77120/no-code-is-good-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vim中避免缩进C++ Namespace</title>
		<link>http://elapsed.yo2.cn/2011/77119/vim%e4%b8%ad%e9%81%bf%e5%85%8d%e7%bc%a9%e8%bf%9bc-namespace/</link>
		<comments>http://elapsed.yo2.cn/2011/77119/vim%e4%b8%ad%e9%81%bf%e5%85%8d%e7%bc%a9%e8%bf%9bc-namespace/#comments</comments>
		<pubDate>Sat, 31 Dec 2011 06:36:45 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[未分类]]></category>
		<category><![CDATA[Vim]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77119</guid>
		<description><![CDATA[Vim的cinoptions不支持定制C++的namespace缩进。不过，可以重写indent文件，把下面这个文件放到vimfiles/indent目录，即可。 cpp.vim如下： " Only load this indent file when no other was loaded. if exists("b:did_indent") finish endif let b:did_indent = 1" Set the function to do the work. setlocal indentexpr=GetCppIndent()" Only define the function once. if exists("*GetCppIndent") finish endiffunction GetCppIndent() let indent = cindent(v:lnum) if v:lnum &#60; 2 return indent endif " Don't indent namespace [...]]]></description>
			<content:encoded><![CDATA[<p>Vim的cinoptions不支持定制C++的namespace缩进。不过，可以重写indent文件，把下面这个文件放到vimfiles/indent目录，即可。<br>
cpp.vim如下：</p>
<pre>
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
  finish
endif
let b:did_indent = 1<br>" Set the function to do the work.
setlocal indentexpr=GetCppIndent()<br>" Only define the function once.
if exists("*GetCppIndent")
  finish
endif<br>function GetCppIndent()
  let indent = cindent(v:lnum)
  if v:lnum &lt; 2
    return indent
  endif<br>  " Don't indent namespace block.
  let prev = v:lnum - 1
  let pline = getline(prev)
  if pline =~ '^\s*namespace\s\+\a\+\s*{\s*$'
    let indent = indent - &amp;sw
  elseif pline =~ '^\s*{\s*$'
    if getline(prev - 1) =~ '^\s*namespace\s\+\a\+\s*$'
      let indent = indent - &amp;sw
    endif
  endif
  return indent
endfunction
</pre>
<p>用VC的同学就比较悲催了：http://blog.csdn.net/ganxinjiang/archive/2010/09/13/5880541.aspx</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2011/77119/vim%e4%b8%ad%e9%81%bf%e5%85%8d%e7%bc%a9%e8%bf%9bc-namespace/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在Vim里给utf-8编码的文件加上BOM</title>
		<link>http://elapsed.yo2.cn/2011/77117/%e5%9c%a8vim%e9%87%8c%e7%bb%99utf-8%e7%bc%96%e7%a0%81%e7%9a%84%e6%96%87%e4%bb%b6%e5%8a%a0%e4%b8%8abom/</link>
		<comments>http://elapsed.yo2.cn/2011/77117/%e5%9c%a8vim%e9%87%8c%e7%bb%99utf-8%e7%bc%96%e7%a0%81%e7%9a%84%e6%96%87%e4%bb%b6%e5%8a%a0%e4%b8%8abom/#comments</comments>
		<pubDate>Tue, 27 Dec 2011 08:20:49 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[未分类]]></category>
		<category><![CDATA[Vim]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77117</guid>
		<description><![CDATA[BOM: Byte Order Mark 简单来说，设置bomb选项（bomb/nobomb）。 今天编辑cue文件，中文曲目在foobar里不能正常显示，google后发现foobar是支持utf-8编码的cue文件的，只是要加上BOM。 于是在Vim里:h bom，得到方法如上。]]></description>
			<content:encoded><![CDATA[<p>BOM: Byte Order Mark<br>
简单来说，设置bomb选项（bomb/nobomb）。</p>
<p>今天编辑cue文件，中文曲目在foobar里不能正常显示，google后发现foobar是支持utf-8编码的cue文件的，只是要加上BOM。</p>
<p>于是在Vim里:h bom，得到方法如上。</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2011/77117/%e5%9c%a8vim%e9%87%8c%e7%bb%99utf-8%e7%bc%96%e7%a0%81%e7%9a%84%e6%96%87%e4%bb%b6%e5%8a%a0%e4%b8%8abom/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C++函数字符串参数的优化</title>
		<link>http://elapsed.yo2.cn/2011/77118/cpp-string-parameter-optimization/</link>
		<comments>http://elapsed.yo2.cn/2011/77118/cpp-string-parameter-optimization/#comments</comments>
		<pubDate>Mon, 28 Nov 2011 09:13:08 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77118</guid>
		<description><![CDATA[介绍了Google Chromium的StringPiece和LLVM的StringRef。]]></description>
			<content:encoded><![CDATA[<p>C++的字符串类一般都可以从C字符串隐式地（implicitly）构造，比如std::string之于const char*。所以，以字符串对象为参数的函数也可以接受C字符串，等等。缺点是会产生临时字符串对象。<br>
[code lang="cpp"]<br>
void findByName(const std:string&amp; name) {<br>
// ...<br>
}<br>
findByName("adam"); // 这里会产生一个临时std::string对象。<br>
[/code]<br>
有时候，这种临时对象是不可避免的，假如findByName在内部只接受和处理std::string，这时，即使参数上是const char*，到了里面还是要生成std::string对象。但是很多时候，这种临时对象都是可以避免的，std::string做得就很好，比如成员函数compare，它既接受const std::string&amp;也接受const char*，find等成员函数也是如此。</p>
<p>假如你想写一个函数，把字符串转换成bool：<br>
[code lang="cpp"]<br>
// "true"/"false" (case ignored) -&gt; true/false<br>
// "1"/"0" -&gt; true/false<br>
... BoolFromString(...)<br>
[/code]<br>
首先提供参数为const char*的实现：<br>
[code lang="cpp"]<br>
bool BoolFromString(const char* input, bool* output) {<br>
if (_stricmp(input, "true") == 0 || strcmp(input, "1") == 0) {<br>
*output = true;<br>
return true;<br>
} else if (_stricmp(input, "false") == 0 || strcmp(input, "0") == 0) {<br>
*output = false;<br>
return true;<br>
}<br>
return false;<br>
}<br>
[/code]<br>
然后，参数为const std::string&amp;的实现只是简单的转调：<br>
[code lang="cpp"]<br>
bool BoolFromString(const std::string&amp; input, bool* output) {<br>
return BoolFromString(input.c_str(), output);<br>
}<br>
[/code]<br>
这里很容易犯的错误是把转调的关系搞反：<br>
[code lang="cpp"]<br>
bool BoolFromString(const char* input, bool* output) {<br>
return BoolFromString(std::string(input), output);<br>
}<br>
[/code]<br>
这样的实现没有任何意义，干脆不要提供const char*的实现。</p>
<p>这种字符串参数的优化看似微不足道，但不可小觑，对于频繁处理字符串的程序（比如编译器），必然是可观的性能改进。</p>
<p>这种改进实施起来对程序员的素质要求比较高，于是就有了辅助的手段：Goolge Chromium提供了StringPiece类，而LLVM则提供了StringRef类。</p>
<p>StringPiece是一个指向一片固定大小内存的类字符串对象（注释原文：A string-like object that points to a sized piece of memory），类的定义大致如下：<br>
[code lang="cpp"]<br>
class StringPiece {<br>
public:<br>
typedef size_t size_type;<br>
private:<br>
const char* ptr_;<br>
size_type length_;<br>
public:<br>
StringPiece() : ptr_(NULL), length_(0) { }<br>
StringPiece(const char* str)<br>
: ptr_(str), length_((str == NULL) ? 0 : strlen(str)) { }<br>
StringPiece(const std::string&amp; str)<br>
: ptr_(str.data()), length_(str.size()) { }<br>
StringPiece(const char* offset, size_type len)<br>
: ptr_(offset), length_(len) { }<br>
const char* data() const { return ptr_; }<br>
size_type size() const { return length_; }<br>
size_type length() const { return length_; }<br>
bool empty() const { return length_ == 0; }<br>
[/code]<br>
StringPiece可由const char*，std::string或者一块非'\0'结尾的字符数组构造，但它不会拷贝内存。这就是它的特点。它所指向的内存必须要有较其自身更长的生命期，这便决定了它比较适合作为函数参数，而不是作为成员变量或者放在容器中。</p>
<p>使用StringPiece来实现BoolFromString，不必有两个函数，一个就够了：<br>
[code lang="cpp"]<br>
bool BoolFromString(const StringPiece&amp; input, bool* output) {<br>
if (_stricmp(input.data(), "true") == 0 || strcmp(input.data(), "1") == 0) {<br>
*output = true;<br>
return true;<br>
} else if (_stricmp(input.data(), "false") == 0 || strcmp(input.data(), "0") == 0) {<br>
*output = false;<br>
return true;<br>
}<br>
return false;<br>
}<br>
[/code]<br>
这个BoolFromString可以接受const char*，std::string，甚至StringPiece，不管哪一种，都没有关于字符串的多余的内存分配和释放。</p>
<p>LLVM的StringRef，实现上跟Google的StringPiece一样，接口方面略有差别。作为参数时，Google建议StringPiece用const&amp;，而LLVM则直接传值。StringRef也可以传const&amp;，但是它的名字起得不好，已经带有引用的含义了（Ref），效率上当然传const&amp;更高。所以我更喜欢Google的StringPiece。</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2011/77118/cpp-string-parameter-optimization/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>启发式思维之倒推法两例</title>
		<link>http://elapsed.yo2.cn/2011/77116/heuristics-working-backwards-examples/</link>
		<comments>http://elapsed.yo2.cn/2011/77116/heuristics-working-backwards-examples/#comments</comments>
		<pubDate>Wed, 23 Nov 2011 07:49:12 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[思维方法]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77116</guid>
		<description><![CDATA[例一：用9升的桶和4升的桶各一个从河里取6升的水。
例二：100根火柴，两个人轮流取，每人每次只能取1~7根，谁拿到最后一根谁赢；问有必胜策略吗？有的话，是先手还是后手必胜？]]></description>
			<content:encoded><![CDATA[<blockquote>
<p>倒推法之所以是一种极为深刻的思维方法，本质上是因为它充分利用了题目中一个最不易被察觉到的信息——结论。</p>
</blockquote>
<p><a href="http://mindhacks.cn/">刘未鹏</a><a href="http://book.douban.com/subject/6709809/">《暗时间》</a>132页提及的两个例子。</p>
<p><strong>例一：用9升的桶和4升的桶各一个从河里取6升的水。</strong></p>
<p>思维过程：6 = 9 - 3，9已知，3未知，3 = 4 - 1，4已知，1未知，1 = 9 - 4 - 4，9和4皆已知。</p>
<p>所以解法是：用9升的桶取9升水，往4升的桶里倒两次，余1升水，存在4升的桶中；再用9升的桶取9升水，把已经有1升水的4升的桶倒满，则9升的桶中便剩6升水。</p>
<p>当然， 反向推导时，也有可能走偏。比如我一开始就这样思考的：6 = 5 +１，５ = 9 - 4，1 = 5 - 4，这一过程中5 + 1难以实现，因为9升的桶和4升的桶分别只有一个，如果有多个，还是可行的。</p>
<p><strong>例二：100根火柴，两个人轮流取，每人每次只能取1~7根，谁拿到最后一根谁赢；问有必胜策略吗？有的话，是先手还是后手必胜？</strong></p>
<p>设两个人为A和B，假定B必胜，先考虑最后一步（第N步）， 每一步都是A先取，B后取。反向推导过程如下：</p>
<p>第N步：必须为<strong>8</strong>根，这样A随便怎么取（1~7根），B都可以一次将剩余的取完。</p>
<p>第N-1步：要想第N步有8根，B取之前必须为9~15根，那么A取之前必须为<strong>16</strong>根。</p>
<p>第N-2步：要想第N-1步有16根，B取之前必须为17~23根，那么A取之前必须为<strong>24</strong>根。</p>
<p>第N-3步：要想第N-2步有24根，B取之前必须为25~31根，那么A取之前必须为<strong>32</strong>根。</p>
<p>......</p>
<p>这时，注意到数字8，16，24，32，......都是8的倍数。也就是说，只要B取完后，保证剩下的火柴数为8的倍数就可以了。考虑100根火柴的情况，B想必胜的话，最好B先取4根，剩下96根（8 * 12），然后A取，B再取，保证剩下88根，依此类推。最终B胜。</p>
<p><strong>简单来说</strong>：假定火柴总数为N根，B先取N%8（N模8）根，随后A取m根，B就取8-m根。</p>
<p>如果A不知道此必胜方法，那么即使A先取，B也可能获胜。只要在当中的某步，保证B取后剩余8 * N根即可。</p>
<p>至此，我们没有考虑<strong>N为8的倍数</strong>的情况，当总数为8的倍数时，就得让A先取了。</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2011/77116/heuristics-working-backwards-examples/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>为什么C++程序跑不过C程序？</title>
		<link>http://elapsed.yo2.cn/2011/77113/cpp-c-performance/</link>
		<comments>http://elapsed.yo2.cn/2011/77113/cpp-c-performance/#comments</comments>
		<pubDate>Thu, 17 Nov 2011 13:49:57 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[C++]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77113</guid>
		<description><![CDATA[C++程序的性能问题往往来源于程序员的误用。比如在不知不觉中创建和销毁很多（不必要的）对象，而程序员往往并不能意识到这一点。当然，容易误用的语言不是一门好的语言。]]></description>
			<content:encoded><![CDATA[<p>C++程序的性能问题往往来源于程序员的误用。比如在不知不觉中创建和销毁很多（不必要的）对象，而程序员往往并不能意识到这一点。当然，容易误用的语言不是一门好的语言。</p>
<p>举个例子，让你按行读取文本文件，你会怎么写？使用全局函数std::getline()吗？</p>
<p>[code lang="cpp" title="代码1"]<br>
void read_line_by_line(std::istream&amp; is) {<br>
std::string line;<br>
while (std::getline(is, line)) {<br>
std::cout &lt;&lt; line &lt;&lt; std::endl;<br>
}<br>
}<br>
[/code]</p>
<p>还是使用std::istream的成员函数getline()？<br>
[code lang="cpp" title="代码2"]<br>
void read_line_by_line(std::istream&amp; is) {<br>
char line[BUFFER_SIZE];<br>
while (is.getline(line, BUFFER_SIZE)) {<br>
std::cout &lt;&lt; line &lt;&lt; std::endl;<br>
}<br>
}<br>
[/code]</p>
<p>有趣的是，使用成员函数的代码反倒是C风格，使用全局函数的代码却是C++风格。从性能上看，代码2比代码1要高效得多，因为代码1每读一行就会重新分配一次存储空间（有待验证），而代码2则不必如此。</p>
<p>代码2的缺点在于常量BUFFER_SIZE。如果BUFFER_SIZE小于某行<u style=display:none>有暗香盈袖</u>长度，while循环便会在读取这一行时退出。性能的提升往往伴随着灵活性的下降。</p>
<p>当然，性能和灵活性并非总是反比的关系，灵活性不好时，性能也未必高。下面这个函数，取自我们实际的项目（便于阐述，略作改动），它在日期时间字符串之间做转换，并且输入输出格式恒定。<br>
[code lang="cpp"]<br>
// \param date "yyyymmdd", such as "20100114"<br>
// \param time "hhmmss", such as "090251"<br>
// \param dateTime "yyyy-MM-ddThh:mm:ss", such as 2010-01-14T09:02:51<br>
const std::string&amp; GmtFromDicomDataTime(const std::string&amp; date,<br>
const std::string&amp; time,<br>
std::string&amp; dateTime)<br>
{<br>
dateTime = date.substr(0, 4) + "-" +<br>
date.substr(4, 2) + "-" +<br>
date.substr(6, 2) + "T" +<br>
time.substr(0, 2) + ":" +<br>
time.substr(2, 2) + ":" +<br>
time.substr(4, 2);<br>
return dateTime;<br>
}<br>
[/code]</p>
<p>这个函数共调用了6次substr()，和10次+操作符。让我们来算算共有多少个临时的字符串。6次substr()调用产生6个临时字符串，10次+操作符产生9个临时字符串，所以一共是15个。我想，没有比这个再多的实现了。</p>
<p>如果要优化的话，鉴于格式恒定，可以先分配好输出字符串的存储，然后将输入字符串的各部分对应到输出字符串中。<br>
[code lang="cpp"]<br>
const std::string&amp; GmtFromDicomDataTime(const std::string&amp; date,<br>
const std::string&amp; time,<br>
std::string&amp; dateTime)<br>
{<br>
dateTime.resize(20);<br>
memcpy(&amp;dateTime[0], &amp;date[0], 4);<br>
dateTime[4] = '-';<br>
memcpy(&amp;dateTime[5], &amp;date[4], 2);<br>
dateTime[7] = '-';<br>
memcpy(&amp;dateTime[8], &amp;date[6], 2);<br>
dateTime[10] = 'T';<br>
memcpy(&amp;dateTime[11], &amp;time[0], 2);<br>
dateTime[13] = ':';<br>
memcpy(&amp;dateTime[14], &amp;time[2], 2);<br>
dateTime[16] = ':';<br>
memcpy(&amp;dateTime[17], &amp;time[4], 2);<br>
return dateTime;<br>
}<br>
[/code]</p>
<p>如代码所示，灵活性没有任何改进，但是毋庸置疑的是，性能肯定有所提高，因为现在不会产生任何临时字符串了。</p>
<p>回到按行读取文本文件的例子，下面这个函数取自一个代码格式化工具，AStyle。我是无意中看到这段代码的，颇为震惊，因为它每次只读一个字符，并且每读一个字符后就调用一次std::string的append()。</p>
<p>[code lang="cpp" title="astyle_main.cpp"]<br>
template&lt;typename T&gt;<br>
string ASStreamIterator&lt;T&gt;::peekNextLine()<br>
{<br>
// ...<br>
// read the next record<br>
inStream-&gt;get(ch);<br>
while (!inStream-&gt;eof() &amp;&amp; ch != '\n' &amp;&amp; ch != '\r')<br>
{<br>
nextLine_.append(1, ch);<br>
inStream-&gt;get(ch);<br>
}<br>
// ...<br>
}<br>
[/code]<br>
跟这段代码相比，使用std::getline()实在太高效了。有些开源软件的代码质量，实在不敢恭维。</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2011/77113/cpp-c-performance/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Test Ajax With JQuery in Google App Engine</title>
		<link>http://elapsed.yo2.cn/2011/77110/ajax-jquery-gae/</link>
		<comments>http://elapsed.yo2.cn/2011/77110/ajax-jquery-gae/#comments</comments>
		<pubDate>Tue, 15 Nov 2011 09:20:21 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77110</guid>
		<description><![CDATA[使用JQuery测试AJAX在Google App Engine中的用法。 如下图所示，在左边文本框输入一些文字，keyup时右边的文本框也显示相应的文字（以{}包裹）。 [code title="app.yaml"] application: ajaxecho version: 1 runtime: python api_version: 1 handlers: - url: /.* script: echo.py [/code] [code lang="python" title="echo.py"] import os from django.utils import simplejson from google.appengine.ext import webapp from google.appengine.ext.webapp import template from google.appengine.ext.webapp import util # ... class EchoHandler(webapp.RequestHandler): def get(self): template_values = {} path = os.path.join(os.path.dirname(__file__), "echo.html") [...]]]></description>
			<content:encoded><![CDATA[<p>使用JQuery测试AJAX在Google App Engine中的用法。<br>
如下图所示，在左边文本框输入一些文字，keyup时右边的文本框也显示相应的文字（以{}包裹）。</p>
<p><a href="http://files.blogcn.com/wp02/M00/02/A6/wKgKC07CLZkAAAAAAAAIsEZoTx0941.png" rel="lightbox[77110]"><img src="http://files.blogcn.com/wp02/M00/02/A6/wKgKC07CLZkAAAAAAAAIsEZoTx0941.png" alt="Ajax Echo GAE" title="ajax_echo_gae" width="578" height="51" class="size-full wp-image-77111"></a></p>
<p>[code title="app.yaml"]<br>
application: ajaxecho<br>
version: 1<br>
runtime: python<br>
api_version: 1<br>
handlers:<br>
- url: /.*<br>
script: echo.py<br>
[/code]</p>
<p>[code lang="python" title="echo.py"]<br>
import os<br>
from django.utils import simplejson<br>
from google.appengine.ext import webapp<br>
from google.appengine.ext.webapp import template<br>
from google.appengine.ext.webapp import util<br>
# ...<br>
class EchoHandler(webapp.RequestHandler):<br>
def get(self):<br>
template_values = {}<br>
path = os.path.join(os.path.dirname(__file__), "echo.html")<br>
self.response.out.write(template.render(path, template_values))<br>
def post(self):<br>
send_value = self.request.get("sendValue")<br>
# Wrap the value with {} as the echo value.<br>
self.response.out.write(simplejson.dumps({ "echoValue": "{ %s }" % send_value }))<br>
def main():<br>
app = webapp.WSGIApplication([<br>
('/', EchoHandler),<br>
], debug=True)<br>
util.run_wsgi_app(app)<br>
if __name__ == '__main__':<br>
main()<br>
[/code]</p>
<p>[code lang="html" title="echo.html" smarttabs="true"]<br>
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"<br>
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;<br>
&lt;html&gt;<br>
&lt;head&gt;<br>
&lt;meta http-equiv="Content-type" content="text/html; charset=utf-8" /&gt;<br>
&lt;title&gt;Ajax Echo&lt;/title&gt;<br>
&lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript" charset="utf-8"&gt;&lt;/script&gt;<br>
&lt;script&gt;<br>
$(function() {<br>
$('#txtA').keyup(function() {<br>
// Call the function to handle the AJAX.<br>
// Pass the value of the text box to the function.<br>
sendValue($(this).val());<br>
});<br>
});<br>
// Function to handle ajax.<br>
function sendValue(txt) {<br>
$.post(<br>
"/",<br>
{ sendValue : txt },<br>
// Function that will be called when server returns a value.<br>
function(data) {<br>
$('#txtB').val(data.echoValue)<br>
},<br>
// The format of the data returned from the server.<br>
"json");<br>
}<br>
&lt;/script&gt;<br>
&lt;/head&gt;<br>
&lt;body&gt;<br>
&lt;label for="txtA"&gt;Type here : &lt;/label&gt;<br>
&lt;input type="text" name="txtA" value="" id="txtA" &gt;<br>
&lt;label for="txtB"&gt;Echo here : &lt;/label&gt;<br>
&lt;input type="text" name="txtB" value="" id="txtB" &gt;<br>
&lt;/body&gt;<br>
&lt;/html&gt;<br>
[/code]</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2011/77110/ajax-jquery-gae/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pretty Python（可爱的Python）</title>
		<link>http://elapsed.yo2.cn/2011/77107/pretty-python/</link>
		<comments>http://elapsed.yo2.cn/2011/77107/pretty-python/#comments</comments>
		<pubDate>Mon, 14 Nov 2011 08:34:23 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Functional]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77107</guid>
		<description><![CDATA[这些slides并不是为了教你怎么入门，所以没有语法、开发环境、应用场景等方面的内容。它侧重于语言层面上的特性，通过一些简单的例子，说明 Python的强大和优雅。这里也提到了高阶函数（higher-order function）和列表推导（list comprehension），借此可以看到Python对函数式语言的借鉴。]]></description>
			<content:encoded><![CDATA[<p><a title="PDF in Google Doc" href="https://docs.google.com/open?id=0B_Q-G4LIwTXKOWJmNGIyYTAtM2Y2ZC00NThkLThiZDgtNDdmMzM4MDk0NWYz" target="_blank">Pretty Python.pdf</a></p>
<p>这些slides并不是为了教你怎么入门，所以没有语法、开发环境、应用场景等方面的内容。它侧重于语言层面上的特性，通过一些简单的例子，说明Python的强大和优雅。这里也提到了高阶函数（higher-order function）和列表推导（list comprehension），借此可以看到Python对函数式语言的借鉴。</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2011/77107/pretty-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Open and Read Files Safely in Python</title>
		<link>http://elapsed.yo2.cn/2011/77106/python-open-read-files/</link>
		<comments>http://elapsed.yo2.cn/2011/77106/python-open-read-files/#comments</comments>
		<pubDate>Mon, 14 Nov 2011 08:28:55 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77106</guid>
		<description><![CDATA[如何安全地打开文件和读取文件，以及优美地处理错误。]]></description>
			<content:encoded><![CDATA[<p>以读取文件为例，如下代码来自 <a rel="nofollow" href="http://diveintopython.org/">Dive Into Python</a> 6.2.3。用书里的话说，“下面这个例子展示了如何安全地打开文件和读取文件，以及优美地处理错误。”<br>
[code lang="python"]<br>
try:<br>
fsock = open(filename, "rb", 0)<br>
try:<br>
fsock.seek(-128, 2)<br>
tagdata = fsock.read(128)<br>
finally:<br>
fsock.close()<br>
except IOError:<br>
pass<br>
[/code]</p>
<p>注意里面那个try没有except IOError，因为外面已经有了。所以下面这种写法有点多余：<br>
[code lang="python"]<br>
try:<br>
fsock = open(filename, "rb", 0)<br>
try:<br>
fsock.seek(-128, 2)<br>
tagdata = fsock.read(128)<br>
except IOError: # 此处多余！<br>
# ...<br>
finally:<br>
fsock.close()<br>
except IOError:<br>
pass<br>
[/code]</p>
<p>引入with语句之后的Python，不需要考虑关闭文件，写法更为简洁：<br>
[code lang="python"]<br>
try:<br>
with open(filename, "rb", 0) as fsock:<br>
fsock.seek(-128, 2)<br>
tagdata = fsock.read(128)<br>
except IOError:<br>
pass<br>
[/code]<br>
可以说，with语句就是Python里的RAII。</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2011/77106/python-open-read-files/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>The Taste of Language（语言的味道）</title>
		<link>http://elapsed.yo2.cn/2011/77103/taste-of-language/</link>
		<comments>http://elapsed.yo2.cn/2011/77103/taste-of-language/#comments</comments>
		<pubDate>Mon, 14 Nov 2011 08:23:46 +0000</pubDate>
		<dc:creator>春庭</dc:creator>
				<category><![CDATA[Functional]]></category>
		<category><![CDATA[Haskell]]></category>
		<category><![CDATA[Lisp]]></category>
		<category><![CDATA[Lua]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://elapsed.yo2.cn/?p=77103</guid>
		<description><![CDATA[The Taste of Language.pdf Agenda 一种语言，方式多样 语言不同，风格迥异 风格虽同，思想有别 尾调用/递归 函数作为第一级对象 这些slides可以作为函数式编程的入门。]]></description>
			<content:encoded><![CDATA[<p><a title="PDF in Google Doc" href="https://docs.google.com/open?id=0B_Q-G4LIwTXKODEzNWQwOTktNDJmYS00YzA5LWEzM2ItMTExYzc4MDg2OTRj" target="_blank">The Taste of Language.pdf</a></p>
<p><strong>Agenda</strong></p>
<ul>
<li>一种语言，方式多样</li>
<li>语言不同，风格迥异</li>
<li>风格虽同，思想有别</li>
<li>尾调用/递归</li>
<li>函数作为第一级对象</li>
</ul>
<p>这些slides可以作为函数式编程的入门。</p>
]]></content:encoded>
			<wfw:commentRss>http://elapsed.yo2.cn/2011/77103/taste-of-language/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

