【问题标题】:How To Optimize This find_if Code?如何优化这个 find_if 代码?
【发布时间】:2010-11-11 01:11:49
【问题描述】:

我有检查字符串是否只包含字母数字和下划线字符的功能...

inline bool IsValidChar(char x) 
{ 
    return (isalnum(x) || (x == '_')); 
} 

我的 find_if 代码是:

if(find_if(str.begin(), str.end(), IsValidChar) != str.end()) 
{ 
    ... 
} 

我只是想把IsValidChar函数去掉,直接把它的内容放到find_if这行代码里。。

【问题讨论】:

  • 为什么?你已经内联了它。
  • 对不起,请忽略内联
  • 你的代码很好。没关系。您可以使用std::string 的成员函数,但这也很好。这里没有任何问题,也没有什么需要修复的。
  • @wilhelmtell - 除了逻辑之外,没有任何问题......在输入“aaaa”上尝试一下,看看会发生什么。
  • @SteveTownsend 哎呀。你是对的。 if( find_if(str.begin(), str.end(), IsInvalidChar) == str.end() ) { /* ... */ }

标签: c++


【解决方案1】:

你基本上是在寻找C++0x lambda expressions:

if (find_if(str.begin(), str.end(),
    [](char x) { return (isalnum(x) || x == '_'); })
    != str.end()) {
    // Do something.
} 

【讨论】:

  • 其实我只是一个C++初学者,可以给个示例代码吗?
  • 查看我的更新答案。您还可以在上述维基百科链接中找到很好的示例。
  • 上次我检查 C++0x 还没有出来。所以,这是在挥手。它不是标准的 C++,也不会在任何标准编译器上编译。
  • @wilhelmtell,你知道 lambdas 已经 supported by a few compilers,对吧?
  • @wilhelmtell:它在 MSVC10 上编译得很好,而且我已经编写了数万行使用这些功能的代码。
【解决方案2】:

Frédéric Hamidi 为您提供了一个很好的示例,说明如何使用 lambda 表达式来完成您真正提出的要求。但是,问题的标题是“如何优化这个 find_if 代码”(强调我的)。匿名函数和命名函数之间的性能差异可以忽略不计(希望为零!)。理想情况下,任何一个版本都可以完全内联(假设 find_if 是内联的),甚至 lambda 表达式和命名函数之间的细微差别也无关紧要。

如果(这是一个很大的如果)您已经分析了您的代码并发现这个表达式是性能瓶颈的根源,那么您可能想要探索另一种算法来获得相同的结果。由于这是一个如此简单的测试(并且不太可能进一步简化),因此您需要研究如何在更高级别上降低此测试的频率。

【讨论】:

  • +1,lambda 不一定比好的老式手工代码更快(不过它们通常更短)。
【解决方案3】:

标准 C++

标准 C++ 方法从 <functional> 标头开始,但它并不能提供所需的一切。我们必须or 两个谓词条件,尽管 SGI 的 STL(以及因此的 GCC)和其他人将其作为名为“compose2”的扩展提供,但如果您的编译器缺少这样的功能,那么您可以从以下位置复制实现(并在)http://accu.org/index.php/journals/443.

使用compose2,你可以写:

#include <functional>
#include <ext/functional> // where GNU g++ hides its compose2

find_if(str.begin(), str.end(),
        __gnu_cxx::compose2(
            std::logical_or<bool>(),
            std::ptr_fun(isalnum),
            std::bind1st(std::equal_to<int>(), '_')));

这一切都非常合乎逻辑,但冗长且阅读速度慢。

使用 BOOST 库

领先的非标准 C++“工具箱”库 - BOOST - 提供了多种替代方案。我将说明 Lambda - 请参阅 http://www.boost.org/doc/libs/1_44_0/doc/html/lambda.html。

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
...
    find_if(str.begin(), str.end(),
            bind(isalnum, boost::lambda::_1) || boost::lambda::_1 == '_');

如果您愿意:

...
using namespace boost::lambda;
...
            bind(isalnum, _1) || _1 == '_');

C++0x

FWIW,下一个 C++ 标准(即将发布,并且已经在几个流行编译器的最新版本中部分实现)将为 lambdas 提供更好的内置支持:

...
            [](char x) { return isalnum(x) || x == '_'; });

讨论

考虑到这一切带来了多大的麻烦,您一定想知道是否最好坚持您的开始。大概。不过,如果您有很多地方想要使用它们,并且每个地方的谓词不同,这些 lambda 东西确实会有所帮助。

【讨论】:

    【解决方案4】:

    你可以这样做:

    if(str.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_")!=std::string::npos)
    {
        ...
    }
    

    在这种情况下,我真的不认为这更清楚。 (如果您有更小或更任意的字符集,这种方法是最干净的。)不过,它摆脱了额外的功能。而且,如果它很重要,它与以前的 C++ 标准兼容。

    【讨论】:

    • 虽然它可能要慢得多,构造一个表示集合中字符的布尔数组,然后开始扫描 str 检查每个字符(以避免 O(str.size() * strlen("ABCD. ..")) 从更明显的重复 strchr() 方法中获得的性能)。可能没关系。
    【解决方案5】:

    我看到的主要问题是它定位了第一个 valid 字符。

    如果要排除字符串中的无效数据,似乎需要反转谓词的含义。

    inline bool IsNotValidChar(char x) 
    { 
        return (!isalnum(x) && (x != '_')); 
    }
    
    if(find_if(str.begin(), str.end(), IsNotValidChar) != str.end()) 
    { 
        ... 
    } 
    

    正如其他人所指出的,lambda 表示法使这更简洁,但不一定更干净,也绝对不会更快。

    【讨论】:

      猜你喜欢
      • 2017-07-13
      • 1970-01-01
      • 2010-12-14
      • 2011-11-10
      • 1970-01-01
      • 1970-01-01
      • 2011-03-09
      • 2019-09-14
      • 1970-01-01
      相关资源
      最近更新 更多