昨天,正如我写的那样,有人问过SO
如果我有一个字符串
x='wow'
在python中应用函数add
:x='wow' x.add(x) 'wowwow'
我怎么能用C ++做到这一点?
将add
(不存在)更正为 __add__
(标准
方法)这是一个深刻而有趣的问题,涉及微妙的低
级别细节,高级算法复杂性考虑因素,以及
甚至穿线!,但它以非常简洁的方式制定。
我正在重新发布 the original question 作为我自己,因为我没有机会提供正确的 在它被删除之前回答,我努力恢复原来的问题,所以 我可以帮助增加对这些问题的一般理解,但失败了。
我已将原始标题“select python或C ++”更改为...
答案 0 :(得分:10)
给定的代码段
x = 'wow'
x.__add__( x )
在Python 2.x和Python 3.x中有不同的含义。
在Python 2.x中,字符串默认为窄字符串,每个编码单元一个字节,
对应于基于C ++ char
的字符串。
在Python 3.x中,字符串是宽字符串,保证代表Unicode,
对应于C ++的实际使用
基于wchar_t
的字符串,同样具有未指定的2或4个字节
每个编码单元。
忽略效率 __add__
方法在两个方面都表现相同
Python版本,对应+
的C ++ std::basic_string
运算符
(例如,对于std::string
和std::wstring
),例如引用CPython 3k
documentation:
object.__add__(self, other)
...评估表达式x + y
,其中x
是具有__add__()
方法的类的实例,调用x.__add__(y)
。
举个例子,CPython 2.7代码
x = 'wow'
y = x.__add__( x )
print y
通常会写为
x = 'wow'
y = x + x
print y
并对应于此C ++代码:
#include <iostream>
#include <string>
using namespace std;
int main()
{
auto const x = string( "wow" );
auto const y = x + x;
cout << y << endl;
}
与给出的许多错误答案的主要区别 the original question, 是C ++对应是表达式,而不是更新。
认为方法名称__add__
表示更改可能很自然
字符串对象的值,更新,但关于可观察的
行为Python字符串是不可变字符串。他们的价值观永远不会改变
可以直接在Python代码中观察到。这与Java和Java中的相同
C#,但与C ++的可变std::basic_string
字符串非常不同。
CPython 2.4补充说 the following 优化,仅用于窄字符串:
s = s + "abc"
和s += "abc"
形式的语句中的字符串连接 现在在某些情况下更有效地执行。这种优化 不会出现在其他Python实现中,例如Jython,所以你不应该这样做 依靠它;当你使用join()
字符串方法时,仍然建议使用 想要有效地将大量字符串粘合在一起。 (由Armin Rigo提供。)
听起来可能不是很多,但适用于此优化的地方 从二次时间 O( n 2 )减少连接序列 至线性时间 O( n ),在最终结果的长度 n 中。
首先,优化将连接替换为更新,例如好像
x = x + a
x = x + b
x = x + c
或者就此而言
x = x + a + b + c
替换为
x += a
x += b
x += c
在一般情况下,会有许多对x
的字符串对象的引用
指的是,因为Python字符串对象必须看起来是不可变的,所以第一个
更新分配不能更改该字符串对象。因此,它通常具有
创建一个全新的字符串对象,并将其(引用)分配给x
。
此时x
拥有对该对象的唯一引用。这意味着
对象可以通过附加b
的更新分配进行更新,因为有
没有观察员。同样适用于c
的追加。
这有点像量子力学:你无法观察到这种肮脏的东西 继续,当有可能有人观察时,它永远不会完成 这些阴谋,但你可以推断它必须是由统计数据进行的 你收集性能,因为线性时间与二次时间有很大不同!
如何实现线性时间?好吧,用更新缓冲区的策略相同
在C ++ std::basic_string
中加倍可以完成,这意味着
现有的缓冲区内容只需要在每次缓冲区重新分配时复制,
而不是每个追加操作。这意味着
复制的总费用在最终字符串大小中处于最差线性,在
与总和相同的方式(表示在每个缓冲区复制的成本加倍)
1 + 2 + 4 + 8 + ... + N小于2 * N.
为了在C ++中忠实地重现CPython代码片段,
应该捕获最终结果和表达性质,
还应捕获其性能特征!
将CPython __add__
直接翻译为C ++ std::basic_string
+
失败
可靠地捕获CPython线性时间。 C ++ +
字符串连接
可以由编译器以与CPython相同的方式进行优化
优化。或者不是 - 这意味着一个人告诉初学者
C ++相当于Python线性时间操作,是二次方的
时间 - 嘿,这是你应该用的......
对于性能特征,C ++ +=
是基本答案,但是,这样做
没有捕获Python代码的表达性质。
自然答案是翻译的线性时间C ++ 字符串构建器类
一系列+=
更新的串联表达式,以便Python代码
from __future__ import print_function
def foo( s ):
print( s )
a = 'alpha'
b = 'beta'
c = 'charlie'
foo( a + b + c ) # Expr-like linear time string building.
粗略对应
#include <string>
#include <sstream>
namespace my {
using std::string;
using std::ostringstream;
template< class Type >
string stringFrom( Type const& v )
{
ostringstream stream;
stream << v;
return stream.str();
}
class StringBuilder
{
private:
string s_;
template< class Type >
static string fastStringFrom( Type const& v )
{
return stringFrom( v );
}
static string const& fastStringFrom( string const& s )
{ return s; }
static char const* fastStringFrom( char const* const s )
{ return s; }
public:
template< class Type >
StringBuilder& operator<<( Type const& v )
{
s_ += fastStringFrom( v );
return *this;
}
string const& str() const { return s_; }
char const* cStr() const { return s_.c_str(); }
operator string const& () const { return str(); }
operator char const* () const { return cStr(); }
};
} // namespace my
#include <iostream>
using namespace std;
typedef my::StringBuilder S;
void foo( string const& s )
{
cout << s << endl;
}
int main()
{
string const a = "alpha";
string const b = "beta";
string const c = "charlie";
foo( S() << a << b << c ); // Expr-like linear time string building.
}