核心内容摘要
输入服装各尺码历史销量,预测未来销量,并给出补货数量建议。
徐波 翻译各类报道纷至沓来。
地表附近及邻近空间正发生什么事我根本摸不着头脑。
从各种迹象看亚洲人似乎已经占领了这个地方但到目前为止我还没有看到他们。
我和珍妮正与世隔绝般地在地下古城工作。
这是一个奇妙的既黑又冷的深埋冰底的生态建筑。
庞大的建筑里到处都是寂静的走廊和房间有些房间的用途一望便知如奇形怪状的厕所设施有些就颇显神秘有一个巨大的房间里面是空的但却有三个直径十英尺的金属球不知用什么方法悬于半空。
这三个金属球似乎在提示某个地方有些能源可能处于激活状态但除此之外实在看不出它们还有什么作用而且我们研究的这些史前文物几乎看不出有生命的痕迹。
在这片古老而又黑暗的静谥王国里我们小心翼翼地移动着几乎总是挤在白色灯光的周围和工作区附近。
虽然并不要求保持肃静但我们发现自己总是压低着噪子说话除了表示虔诚之外并没有明显的理由要这么做也许心中有丝淡淡的敬畏甚至是一些害怕吧。
整个队伍都知道我们即将取得突破。
更重要的是官员们也知道了这个情况这也正是他们逼着我们昼夜不停地工作的原因。
珍妮用钩子钩起插到我们正在研究的文物里的电源进行另一项测试。
她注视着监视器突然出现一个尖峰信号接着又什么都没有了。
她丧气甩甩手“又不知什么地方出错了真是难以捉摸啊”她停了下来懊恼地叹了口气。
“至少它有反应这就说明了一些问题”我若有所思“让我们再试试我们昨天试过的电源在哪”她挥挥手“在那儿如果你直接用它成功几率几乎是零即使用上适配器也差不了多少。
这玩意儿换一种能源可能有用如果在原理上不是完全不同的话。
“它有反应”我重复道“我们又靠近了一步。
有时你可以用个旧的把它跟新的混在一起让它工作如果你能选用正确的适配器的话有时你可以再试试……”我和Guru正在进行代码回顾。
当我俩正埋头于代码堆时突然听到一种不同寻常的响声确实是不同寻常至少在办公室里是这样。
我望着Guru“这声音象是……”“……婴儿”我俩异口同声地说道。
我站起身扫视四周发现温迪褐色的卷发正靠近我的卧室。
当她转过拐角我发现她正推着一辆婴儿车车上的小珍妮才满月我们跑过去跟她嘻嘘一番。
虽然平常我并不太喜欢小孩但温迪是我的亲密伙伴对她的小孩我自然要另眼相看。
甚至连鲍勃也加入到人群中。
“好了好了”他气喘吁吁地说道仿佛他是孩子的父亲似的“我总是说小孩子要么象温斯顿.邱吉尔要么象尤达她象谁啊”他把他那杯咖啡放在我办公桌的一叠打印纸上从温迪手中接过珍妮。
他凝视着珍妮还有点皱的脸蛋“嗯…可能是个例外跟我说的不一样。
再过二十年肯定是个大美人。
”此时珍妮不安地挣扎起来发出哼哼声。
鲍勃吸吸鼻把珍妮交回给温迪顾不上他那杯咖啡便转身离去。
我扬了扬眉“看上去你已经教会她怎样对付鲍勃了。
”温迪笑了“我最好改改她的脾气。
”等这个主要魅力人物离去后人群也散开了Guru和我重新开始代码回顾。
我小心翼翼地将鲍勃的咖啡转移到一个安全一点的地方。
一切都还顺利直到我们碰到一个使用了strok的解析函数。
Guru斜视着我。
“有没有比strtok更好的选择我的孩子”她问道。
“但strtok正确地完成了我需要它完成的任务。
您不是老提醒我C库函数也是C标准的一部分吗”“是这样我的学徒工。
但记住C标准对于库函数是不作承诺的。
”“好了如果你想跟我谈论多线程的话题的话…”“不我的孩子”Guru打断说“我说的不是这个问题。
神圣的C标准明确指出strtok是不可再入的事实上没有一个标准C函数能够做到这点。
更糟的是在你这个情况strtok在各级调用中都保持全局状态考虑一下你的函数所运行的上下文环境它在另一个解析函数的内部使用而该解析函数也可能使用strtok。
”我用我最擅长的如车灯前的小鹿般的目光注视着她让她明白我没怎么搞懂。
Guru点点头“考虑一个类似的例子”她边说边拿起干擦笔在我的白书写板写道void f(){// ...strtok( charPtrA, delimiters );// ...strtok( 0, delimiters );// ...}void g(){strtok( charPtrB, otherDelimiters );// ...f();// ...strtok( 0, otherDelimiters );}“当f()结束的时候它就会使stortok内部状态变得无法预料g()就无法以它所预期的方式使用它。
”“我明白了”我说“所以解决方法是……”我还不想把皮球接过来。
“用你自己的算法在必要时做些改变。
例如这里是基本的strtok函数它在std::string上操作而不是C风格的字符串并经过修改使之不改变源字符串。
”std::string StringTok(const std::string* newSeq,const std::string delim ){// 象标准的strtok一样在各级调用中保持全局状态。
// 要搜索的序列static const std::string* seq 0;// 当前位置static std::string::size_type pos 0;if( newSeq ){seq newSeq;pos 0;}std::string token;if( seq pos ! std::string::npos ){pos seq-find_first_not_of( delim.c_str(), pos );if( pos ! std::string::npos ){std::string::size_type next seq-find_first_of( delim.c_str(), pos );token seq-substr( pos, next-pos );pos next;if( pos ! std::string::npos )pos;if( pos seq-size() )pos std::string::npos;}}return token;}我承认上面这些并非照抄Guru所写的内容我不得不修改一些错字清除一些逻辑错误。
不过还是言归正传……“这是它的实现”Guru继续道“它受到与我们前面所讨论的标准C strtok函数相同的大部分限制并且带来了另外一些令人不安的东西象创建一份标志字符串的负担。
”“但是难道不能采用copy-on-write吧有些string的实现不是印证了这个情况也就是说返加值是现存string的一个子串而且仅作了一个copy-on-write?”“有些实现可能是这样做的但是已有名言‘用copy-on-write使实现方法很难做到线程安全[2]。
’现在更多的实现方法遵循了该指示去除了这些优化措施。
另外这并不是唯一的问题该函数在异常安全方面做得也不好比如在子串赋值给抛出的token的情况下。
”“所以”我继续道“我怎样克服各方面的问题呢”Guru微斜的唇角流出一丝笑意“我的孩子如果你打算超越学徒工这个阶段你必须自己解决问题。
克服这些问题的方法应该是象我确信你的大学课本曾说过的此题留作习题。
写一个StringTok函数消除可再入性和嵌套问题修正异常安全问题并尽可能地通过避免字符串拷贝来保持效率。
下午我来看你的答案。
”她转身离去我接着干活。
不久我就意识到要保持状态必须要使用一个对象。
我写了个测试程序并为我的解决方案写出代码修正了一些bug后我回头去找Guru。
我最终交给她的代码如下templateclass Tclass StringTok{public:StringTok( const T seq,typename T::size_type pos 0 ): seq_( seq ) , pos_( pos ) { }T operator()( const T delim );private:const T seq_;typename T::size_type pos_;};templateclass TT StringTokT::operator()( const T delim ){T token;if( pos_ ! T::npos ){// 开始寻找标志typename T::size_type first seq_.find_first_not_of( delim.c_str(), pos_ );if( first ! T::npos ){// 所找到标志的长度typename T::size_type num seq_.find_first_of( delim.c_str(), first ) - first;// 把所有的工作放在此处token seq_.substr( first, num );//完成现在提交只采用不抛出异常的操作pos_ firstnum;if( pos_ ! T::npos ) pos_;if( pos_ seq_.size() ) pos_ T::npos;}}return token;}“用这种方法”我解释道“代码更简单了因为我不必担心开始新序列。
如果用户需要他只需分创建一个新的tokenizer对象。
现在异常安全的bug也消除了因为我‘把所有的工作放在固定的地方在提交时只采用不抛出异常的操作。
’哦我想应该使它模板化……并不需要增加多少工作只要多写几个typename罢了用这种方法我们可以用宽字符来使用它。
”Guru压了压耳后一络银灰色的头发。
“我知道了”她狡黠地说“StringTok使用了缺省的拷贝构造函数和赋值操作。
”我笑了这次有充分的思想准备不会上钩。
“事实上”我镇定地回答“我故意保留了缺省拷贝和赋值的用法因为它们起到了书签的作用用这种方法用户可以在标志化过程的任一位置取得StringTok的一份拷贝并可从该位置起探寻另外的标志化方法。
事实上它在你原来的问题程序中工作良好即使你通过使f和g独立解析这个非常相似的字符串来增加问题的难度它也毫无问题。
”void f(){// ...StringTokstd::string x( stringA );// ...x( delimiters );// ...}void g(){// ...// 这次字符串是一样的StringTokstd::string y( stringA );// ...f();// ...// 记住我们的位置StringTokstd::string z( y );y( otherDelimiters );if( DidntWork() )//再进入同一位置z( yetOtherDelimiters );}“不仅各种标志化操作可以并行工作而且它们可以在同一字符串上独立工作。
所有这些”我
总结道“不会增加任何开销因为这些序列的字符串从不会被实际拷贝。
”我双手抱胸又坐了下来。
“说得好”Guru微笑颔首“当你进一步深入时你可能考虑增加更强大的特性象在分隔符中使用vectorstring的能力这样你就不会受限于单字符分隔符。
”“或者”我趁热打铁地说“允许它具有辨认空字段的能力。
我以前曾试过strtok它能解析出以逗号分隔的字段但如果存在空字段象A,B,,,C这样就把它给搞糊涂了strtok将会跳过两个空字段。
”我们快速地对代码回顾作了
总结然后我就开始用修改后的解析函数来工作了。
“要让仪器能分析出这台东西还有些工作要做。
”珍妮摇摇头。
“好了跟上次测试比起来我们还是有收获的。
能量输入的各方面怎么样跟上次我们测试时比不是有变化吗”珍妮点点头“我知道了让我们再试试。
”[参考文献][1] To the tune of Ive Got Rhythm.[2] H. Sutter. More Exceptional C, Items 15 and 16 (Addison-Wesley,