【问题标题】:Algorithm used in Ruby for "String#include?"Ruby 中用于“String#include?”的算法
【发布时间】:2011-12-09 08:35:24
【问题描述】:

是否有人能够确定包含使用的算法? Ruby中的方法?例如

"helloworld".include?("hello")

【问题讨论】:

  • 你知道你可以看看the source,对吧? (然后查看 C 源代码,至少对于 String 的包含。)

标签: ruby algorithm search substring


【解决方案1】:

正如 emboss 在他的回答中所说,String#include 调用 rb_str_index。该函数依次调用rb_memsearch,实现了Rabin-Karp string search algorithm,根据this post on ruby-forum.com

【讨论】:

  • string[substr] 是否调用相同的函数进行搜索?我的意思是同样的算法,同样的速度
【解决方案2】:

Ruby 语言规范没有规定任何特定的算法。每个实现都可以使用他们想要的任何算法。

例如在RubiniusString#include?调用String#find_string

def include?(needle)
  if needle.kind_of? Fixnum
    needle = needle % 256
    str_needle = needle.chr
  else
    str_needle = StringValue(needle)
  end

  !!find_string(str_needle, 0)
end

String#find_string 又通过string_index 原语实现:

def find_string(pattern, start)
  Rubinius.primitive :string_index
  raise PrimitiveFailure, "String#find_string failed"
end

string_index 原语由rubinius::String::index 函数实现:

// Rubinius.primitive :string_index
Fixnum* index(STATE, String* pattern, Fixnum* start);

rubinius::String::index:

Fixnum* String::index(STATE, String* pattern, Fixnum* start) {
  native_int total = size();
  native_int match_size = pattern->size();

  if(start->to_native() < 0) {
    Exception::argument_error(state, "negative start given");
  }

  switch(match_size) {
  case 0:
    return start;
  case 1:
    {
      uint8_t* buf = byte_address();
      uint8_t matcher = pattern->byte_address()[0];

      for(native_int pos = start->to_native(); pos < total; pos++) {
        if(buf[pos] == matcher) return Fixnum::from(pos);
      }
    }
    return nil<Fixnum>();
  default:
    {
      uint8_t* buf = byte_address();
      uint8_t* matcher = pattern->byte_address();

      uint8_t* last = buf + (total - match_size);
      uint8_t* pos = buf + start->to_native();

      while(pos <= last) {
        // Checking *pos directly then also checking memcmp is an
        // optimization. It's about 10x faster than just calling memcmp
        // everytime.
        if(*pos == *matcher &&
            memcmp(pos, matcher, match_size) == 0) {
          return Fixnum::from(pos - buf);
        }
        pos++;
      }
    }
    return nil<Fixnum>();
  }
}

【讨论】:

    【解决方案3】:

    这是String#include?的实际实现:

    static VALUE
    rb_str_include(VALUE str, VALUE arg)
    {
        long i;
    
        StringValue(arg);
        i = rb_str_index(str, arg, 0);
    
        if (i == -1) return Qfalse;
        return Qtrue;
    }
    

    所以实际使用的算法可以在rb_str_index 中找到。

    【讨论】:

    • 又使用rb_memsearch,实现Karp Rabin算法(根据this post)。
    • @rdvdijk:我会把这个作为答案:-)
    • @rdvdijk 不错。你应该回答这个问题。我会删除我的答案。
    • 无需删除!我会参考你的答案。
    猜你喜欢
    • 2020-07-16
    • 2011-09-22
    • 1970-01-01
    • 2012-12-03
    • 2014-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多