2

我看到 Ruby 中有一个相对较新的特性,它允许链式迭代——换句话说,而不是each_with_indices { |x,i,j| ... }你可能做的each.with_indices { |x,i,j| ... }, where#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 语言编写 a#with_indices以链接到上述NMatrix#each方法?

我不希望任何人觉得我是在要求他们为我编写代码,但如果您确实愿意,我们很乐意让您参与我们的项目。=)

但是,如果您知道网络上其他地方的一些示例说明这是如何完成的,那将是完美的——或者如果您可以用文字解释,那也很可爱。

4

2 回答 2

1

#with_index是一种方法Enumeratorhttp ://ruby-doc.org/core-1.9.3/Enumerator.html#method-i-with_index

我想您可以创建一个子类Enumerator#with_indices让您#each返回该类的实例?这是首先想到的事情,尽管您的枚举器可能必须与原始类非常耦合......

于 2013-05-02T17:04:53.927 回答
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它将正常工作,不受您的控制。而且您不需要考虑引入 matrix-specific #with_indices,因为#each它本身并不是真正特定于矩阵,而是特定于任何类型的一维有序集合。最后,很抱歉没有成为一名熟练的 C 程序员来满足您与 C 相关的问题部分。

于 2013-05-02T17:28:11.337 回答