Реализация цепочечных итераторов в расширении Ruby C
Я вижу, что в Ruby есть относительно новая функция, которая позволяет связывать итерации - другими словами, вместо each_with_indices { |x,i,j| ... }
вы могли бы сделать each.with_indices { |x,i,j| ... }
, где #each
возвращает Enumerator
объект и Enumerator#with_indices
вызывает включение дополнительных параметров доходности.
Так, Enumerator
имеет свой метод #with_index
, предположительно для одномерных объектов, источник найден здесь. Но я не могу найти лучший способ приспособить это к другим объектам.
Чтобы было понятно, и в ответ на комментарии: у Ruby нет #each_with_indices
прямо сейчас - это только есть #each_with_index
, (Вот почему я хочу создать один.)
Ряд вопросов, сами прикованные цепью:
- Как можно адаптировать цепную итерацию к одномерному объекту? Просто сделай
include Enumerable
? - Предположительно, вышеприведенный (#1) не будет работать дляn - мерного объекта. Будет ли один создать
EnumerableN
класс, производный отEnumerable
, но с#with_index
конвертирован в#with_indices
? - Можно ли сделать № 2 для расширений Ruby, написанных на C? Например, у меня есть класс матрицы, в котором хранятся различные типы данных (числа с плавающей запятой, двойные числа, целые числа, иногда обычные объекты 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
метод выше?
Я не хочу, чтобы кто-то чувствовал, что я прошу их написать это для меня, хотя, если бы вы захотели, мы бы хотели, чтобы вы участвовали в нашем проекте. знак равно
Но если вы знаете какой-нибудь пример в Интернете, как это делается, это было бы идеально - или если бы вы могли просто объяснить словами, это тоже было бы прекрасно.
2 ответа
#with_index
это метод Enumerator
: http://ruby-doc.org/core-1.9.3/Enumerator.html
Я полагаю, вы могли бы сделать подкласс Enumerator
который имеет #with_indices
и ваш #each
вернуть экземпляр этого класса? Это первое, что приходит на ум, хотя ваш перечислитель может быть довольно тесно связан с исходным классом...
Поскольку вы говорите, что вы также интересуетесь лингвистикой 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| ... }
Этот метод должен возвращать 2-мерный (n-мерный) массив как indices == [i, j]
, Вы не должны идти на версию:
matrix.each_with_indices { |e, i, j| ... }
Для #with_index
метод, это не ваша забота вообще. Если твой NMatrix
обеспечивает #each
метод (что, безусловно, делает), то #with_index
будет работать с ним нормально, вне вашего контроля. И вам не нужно задумываться о введении матриц-специфических #with_indices
, так как #each
сама по себе не специфична для матриц, а для одномерных упорядоченных коллекций любого рода. Наконец, извините за то, что вы не являетесь опытным программистом C, чтобы удовлетворить вашу часть вопроса, связанную с C.