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



我看到Ruby中有一个相对较新的特性,它允许链式迭代——换句话说,不是each_with_indices { |x,i,j| ... },而是each.with_indices { |x,i,j| ... },其中#each返回Enumerator对象,Enumerator#with_indices导致包含额外的yield参数。

所以,Enumerator有自己的方法#with_index,大概是一维对象,源代码在这里找到。但是我想不出最好的办法把它应用到其他物体上。

澄清一下,作为对评论的回应: Ruby现在没有#each_with_indices——它只有#each_with_index。(这就是为什么我想创建一个。)

一系列问题,它们彼此相连:

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

的例子:

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
    }
  }
}

因此,将我的三个问题合并为一个:我如何在C中编写#with_indices以链接到上面的NMatrix#each方法?

我不特别希望任何人觉得我要求他们为我编写代码,尽管如果你确实想要,我们很乐意让你参与我们的项目。=)

但是如果你知道网上其他地方的例子是如何做到的,那就太好了——或者如果你能用语言解释,那也很好。

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

我想你可以做一个子类的Enumerator#with_indices,并有你的#each返回该类的实例?这是我想到的第一件事,尽管枚举器可能必须与原始类紧密耦合…

既然你说你也对Ruby语言学感兴趣,而不仅仅是对C感兴趣,那么让我贡献我的5美分,而不是声称实际上回答了这个问题。#each_with_index#with_index已经变得如此习惯,以至于大多数人都依赖于索引是一个数字。因此,如果您以这种方式去实现NMatrix#each_with_index,那么在{ |e, i| ... }块中它将提供eg。数组[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], ...作为索引i,您会让人们感到惊讶。另外,如果其他人将您的NMatrix#each枚举器与#with_index方法链接起来,他们将只收到一个数字作为索引。因此,确实,您得出的结论是正确的:您需要一种不同的方法来处理2个索引类型(或者,更一般地说,对于高维矩阵,n个索引):

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

这个方法应该返回一个2维(n维)数组,如indices == [i, j]。你不应该选择版本:

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

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

最新更新