我看到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
。(这就是为什么我想创建一个。)
一系列问题,它们彼此相连:
- 如何将链式迭代适应于一维对象?简单地做一个
include Enumerable
? - 假设上述(#1)不适用于n维对象。是否可以创建
EnumerableN
类,派生自Enumerable
,但将#with_index
转换为#with_indices
? - #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_index
是Enumerator
的一个方法: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相关部分的问题。