在C ++中,CPython字符串连接的等价物是什么?

时间:2012-10-23 00:30:18

标签: c++ python

  

可能重复:
  Simple string concatenation

昨天,正如我写的那样,有人问过SO

  

如果我有一个字符串x='wow'   在python中应用函数add

x='wow'
x.add(x)
'wowwow'
     

我怎么能用C ++做到这一点?

add(不存在)更正为 __add__ (标准 方法)这是一个深刻而有趣的问题,涉及微妙的低 级别细节,高级算法复杂性考虑因素,以及 甚至穿线!,但它以非常简洁的方式制定。

我正在重新发布 the original question 作为我自己,因为我没有机会提供正确的 在它被删除之前回答,我努力恢复原来​​的问题,所以 我可以帮助增加对这些问题的一般理解,但失败了。

我已将原始标题“select python或C ++”更改为...

  • 在C ++中,CPython字符串连接的等价物是什么?
从而缩小了这个问题。

1 个答案:

答案 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::stringstd::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中的二次到线性时间优化。

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 ++中的线性时间字符串连接表达式。

为了在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.
}