在Ruby C扩展中实现链式迭代器

时间:2013-05-02 03:27:19

标签: c ruby enumerable

我发现Ruby中有一个相对较新的特性允许链接迭代 - 换句话说,代替each_with_indices { |x,i,j| ... }你可以each.with_indices { |x,i,j| ... },其中#each返回{{1} } object和Enumerator导致包含额外的yield参数。

因此,Enumerator#with_indices有自己的方法Enumerator,大概是对于一维对象source found here。但我无法找到使其适应其他物体的最佳方法。

要明确,并回应评论: Ruby现在没有#with_index - 它只有一个#each_with_indices。 (这就是为什么我要创建一个。)

一系列问题,他们自己被束缚住了:

  1. 如何将链式迭代适应一个维度对象?只需做一个#each_with_index
  2. 据推测,上述(#1)不适用于 n - 维度对象。是否会创建一个include Enumerable类,派生自EnumerableN,但Enumerable转换为#with_index
  3. #2可以用C语言编写的Ruby扩展吗?例如,我有一个矩阵类,它存储各种类型的数据(浮点数,双精度数,整数,有时是常规的Ruby对象,等。)。枚举需要首先检查数据类型(#with_indices),如下例所示。
  4. 示例:

    dtype

    所以,将我的三个问题合并为一个:我如何用C语言写一个VALUE nm_dense_each(VALUE nm) { volatile VALUE nm = nmatrix; // Not sure this actually does anything. DENSE_STORAGE* s = NM_STORAGE_DENSE(nm); // get the storage pointer RETURN_ENUMERATOR(nm, 0, 0); if (NM_DTYPE(nm) == nm::RUBYOBJ) { // matrix stores VALUEs // matrix of Ruby objects -- yield those objects directly for (size_t i = 0; i < nm_storage_count_max_elements(s); ++i) rb_yield( reinterpret_cast<VALUE*>(s->elements)[i] ); } else { // matrix stores non-Ruby data (int, float, etc) // We're going to copy the matrix element into a Ruby VALUE and then operate on it. This way user can't accidentally // modify it and cause a seg fault. for (size_t i = 0; i < nm_storage_count_max_elements(s); ++i) { // rubyobj_from_cval() converts any type of data into a VALUE using macros such as INT2FIX() VALUE v = rubyobj_from_cval((char*)(s->elements) + i*DTYPE_SIZES[NM_DTYPE(nm)], NM_DTYPE(nm)).rval; rb_yield( v ); // yield to the copy we made } } } 链接到上面的#with_indices方法?

    我并不特别希望任何人觉得我要求他们为我编码,但如果你真的想要,我们很乐意让你参与我们的项目。 =)

    但是,如果你在网上其他地方知道如何做到这一点,那就完美了 - 或者如果你能用语言解释,那也很可爱。

2 个答案:

答案 0 :(得分:1)

#with_indexEnumerator的一种方法:http://ruby-doc.org/core-1.9.3/Enumerator.html#method-i-with_index

我想你可以创建一个Enumerator的子类#with_indices并让你的#each返回该类的实例?这是我想到的第一件事,虽然你的普查员可能必须与原始课程相结合......

答案 1 :(得分:0)

因为你说你也对Ruby语言学感兴趣,而不仅仅是C,让我贡献我的5美分,而不是声称实际回答这个问题。 #each_with_index#with_index已经变得如此惯用,以至于大多数人依赖索引作为数字。因此,如果您以这种方式实施NMatrix#each_with_index,那么它将提供例如{ |e, i| ... }。数组[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], ...作为索引i,你会让人大吃一惊。此外,如果其他人使用NMatrix#each方法链接您的#with_index枚举器,则他们只会收到一个数字作为索引。所以,实际上,你应该得出结论,你需要一个独特的方法来处理2个索引类型(或者更常见的是更高维矩阵的n个索引):

matrix.each_with_indices { |e, indices| ... }

此方法应返回二维(n维)数组indices == [i, j]。你不应该使用版本:

matrix.each_with_indices { |e, i, j| ... }

至于#with_index方法,根本不是你关心的问题。如果您的NMatrix提供了#each方法(它确实如此),那么#with_index将会正常使用它,不受您的控制。而且你不需要考虑引入特定于矩阵的#with_indices,因为#each本身并不是特定于矩阵,而是任何类型的一维有序集合。最后,很抱歉没有熟练的C程序员来迎合你问题的C相关部分。