分享
 
 
 

对话:#16: Al-Go-Rithms

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

[声明]:本英文资料源自于Herb Sutter 创建的“Conversation”栏目,“C++ 翻译小组”的翻译作品供学习交流与参考用途,不得用于任何商业用途。未经Herb Sutter、Jim Hyslop同意,不得转载;对于违反以上条款,翻译小组对此不负任何责任;特此声明。

文章来源:http://www.gotw.ca

版权归属:Herb Sutter and Jim Hyslop

译 者:徐波

对话:#16: Al-Go-Rithms [1]

各类报道纷至沓来。地表附近及邻近空间正发生什么事,我根本摸不着头脑。从各种迹象看,亚洲人似乎已经占领了这个地方,但到目前为止,我还没有看到他们。

我和珍妮正与世隔绝般地在地下古城工作。这是一个奇妙的,既黑又冷的,深埋冰底的生态建筑。庞大的建筑里到处都是寂静的走廊和房间,有些房间的用途一望便知(如奇形怪状的厕所设施),有些就颇显神秘(有一个巨大的房间,里面是空的,但却有三个直径十英尺的金属球不知用什么方法悬于半空)。这三个金属球似乎在提示某个地方有些能源可能处于激活状态,但除此之外实在看不出它们还有什么作用,而且我们研究的这些史前文物几乎看不出有生命的痕迹。

在这片古老而又黑暗的静谥王国里,我们小心翼翼地移动着,几乎总是挤在白色灯光的周围和工作区附近。虽然并不要求保持肃静,但我们发现自己总是压低着噪子说话,除了表示虔诚之外,并没有明显的理由要这么做,也许心中有丝淡淡的敬畏,甚至是一些害怕吧。

整个队伍都知道我们即将取得突破。更重要的是,官员们也知道了这个情况,这也正是他们逼着我们昼夜不停地工作的原因。

珍妮用钩子钩起插到我们正在研究的文物里的电源,进行另一项测试。她注视着监视器,突然出现一个尖峰信号,接着又什么都没有了。她丧气甩甩手:“又不知什么地方出错了!真是难以捉摸啊!”她停了下来,懊恼地叹了口气。

“至少它有反应,这就说明了一些问题,”我若有所思,“让我们再试试,我们昨天试过的电源在哪?”

她挥挥手:“在那儿,如果你直接用它,成功几率几乎是零,即使用上适配器也差不了多少。这玩意儿换一种能源可能有用,如果在原理上不是完全不同的话。

“它有反应,”我重复道,“我们又靠近了一步。有时你可以用个旧的,把它跟新的混在一起让它工作,如果你能选用正确的适配器的话,有时你可以再试试……”

- - - - - - - - - - - - - - - - - - - - - - - - -

我和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。我最终交给她的代码如下:

template<class T>

class 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_;

};

template<class T>

T StringTok<T>::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_ = first+num;

if( pos_ != T::npos ) ++pos_;

if( pos_ >= seq_.size() ) pos_ = T::npos;

}

}

return token;

}

“用这种方法,”我解释道,“代码更简单了,因为我不必担心开始新序列。如果用户需要,他只需分创建一个新的tokenizer对象。现在异常安全的bug也消除了,因为我‘把所有的工作放在固定的地方,在提交时只采用不抛出异常的操作。’哦,我想应该使它模板化……并不需要增加多少工作,只要多写几个typename罢了,用这种方法我们可以用宽字符来使用它。”

Guru压了压耳后一络银灰色的头发。“我知道了,”她狡黠地说,“StringTok使用了缺省的拷贝构造函数和赋值操作。”

我笑了,这次有充分的思想准备,不会上钩。“事实上,”我镇定地回答:“我故意保留了缺省拷贝和赋值的用法,因为它们起到了书签的作用:用这种方法用户可以在标志化过程的任一位置取得StringTok的一份拷贝,并可从该位置起探寻另外的标志化方法。事实上,它在你原来的问题程序中工作良好,即使你通过使f和g独立解析这个非常相似的字符串来增加问题的难度,它也毫无问题。”

void f()

{

// ...

StringTok<std::string> x( stringA );

// ...

x( delimiters );

// ...

}

void g()

{

// ...

// 这次字符串是一样的

StringTok<std::string> y( stringA );

// ...

f();

// ...

// 记住我们的位置

StringTok<std::string> z( y );

y( otherDelimiters );

if( DidntWork() )

//再进入同一位置

z( yetOtherDelimiters );

}

“不仅各种标志化操作可以并行工作,而且它们可以在同一字符串上独立工作。所有这些,”我总结道,“不会增加任何开销,因为这些序列的字符串从不会被实际拷贝。”我双手抱胸,又坐了下来。

“说得好,”Guru微笑颔首,“当你进一步深入时,你可能考虑增加更强大的特性,象在分隔符中使用vector<string>的能力,这样你就不会受限于单字符分隔符。”

“或者,”我趁热打铁地说,“允许它具有辨认空字段的能力。我以前曾试过strtok,它能解析出以逗号分隔的字段,但如果存在空字段,象A,B,,,C这样,就把它给搞糊涂了,strtok将会跳过两个空字段。”

我们快速地对代码回顾作了总结,然后我就开始用修改后的解析函数来工作了。

- - - - - - - - - - - - - - - - - - - - - - - - -

“要让仪器能分析出这台东西,还有些工作要做。”珍妮摇摇头。

“好了,跟上次测试比起来,我们还是有收获的。能量输入的各方面怎么样,跟上次我们测试时比不是有变化吗?”

珍妮点点头:“我知道了,让我们再试试。”

[参考文献]

[1] To the tune of "I've Got Rhythm."

[2] H. Sutter. More Exceptional C++, Items 15 and 16 (Addison-Wesley, 2001).

[关于作者]

Herb Sutter

是个独立顾问,也是ISO/ANSI C++标准委员会的秘书。你可通过hsutter@acm.org.联系他

Jim Hyslop

Leitch Technology International Inc.资深的软件设计师,你可通过jim.hyslop@leitch.com联系他

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有