我在Ruby中有数组,我想用.normalize方法扩展它们。这个方法应该修改数组,使得它的所有元素总和为1.这在Ruby中太贵了,所以我想用C和RubyInline来做。
require "rubygems"
require "inline"
class Array
inline do |builder|
builder.c_raw '
static VALUE normalize(VALUE self) {
double total_size = 0, len;
int i;
VALUE* array = RARRAY_PTR(self);
len = RARRAY_LEN(self);
for(i=0; i < len; i++){
total_size += NUM2DBL(array[i]);
}
for(i=0; i < len; i++){
array[i] = INT2NUM(NUM2DBL(array[i])/total_size);
}
return array;
}'
end
end
a = [1,2,0,0,0,0,0,3,0,4]
puts a.normalize.inspect
这导致
$ ruby tmp.rb
tmp.rb:29: [BUG] Segmentation fault
ruby 1.8.7 (2011-06-30 patchlevel 352) [x86_64-linux]
Aborted (core dumped)
编辑:经过一些调试后,崩溃似乎来了
VALUE* array = RARRAY_PTR(self);
答案 0 :(得分:3)
这里有一些事情需要解决:
使用c_raw
时,rubyinline不会尝试检测arity,而是假设您要使用可变数量的参数。您可以覆盖此(pass:arity =&gt; 0)或将方法签名更改为
VALUE normalize(int argc, VALUE *argv, VALUE self)
目前rubyinline假设您的方法具有该签名,因此您可能将整数0重新解释为指针。
接下来,你总是用零填充数组,因为所有的数组元素都是&lt; 1,然后你转换为一个整数,所以你得到0 - 使用rb_float_new
将双重转回红宝石Float
。
最后,您的返回值是错误的,它是VALUE *
而不是VALUE
。您可能希望返回self
。
最后,调用方法normalize!
会更像红宝石。默认情况下,ruby inline从c函数名称中提取方法名称,这当然不允许您使用这样的感叹号。您可以使用method_name
选项覆盖它。
总而言之,我的示例版本看起来像
require "rubygems"
require "inline"
class Array
inline do |builder|
builder.c_raw <<-'SRC', :method_name => 'normalize!', :arity => 0
static VALUE normalize(VALUE self) {
double total_size = 0;
size_t len, i;
VALUE* array = RARRAY_PTR(self);
len = RARRAY_LEN(self);
for(i=0; i < len; i++){
total_size += NUM2DBL(array[i]);
}
for(i=0; i < len; i++){
array[i] = rb_float_new((NUM2DBL(array[i])/total_size));
}
return self;
}
SRC
end
end
a = [1,2,0,0,0,0,0,1,0,4]
puts a.normalize!.inspect