Предположим, у меня есть объект:
obj = Object.new #<Object:0x00007fbe36b4db28>
И я конвертирую его в перечислитель:
obj_enum = obj.to_enum #<Enumerator: #<Object:0x00007fbe36b4db28>:each>
Теперь я хочу вернуть свой объект из перечислителя. Я нашел способ сделать это, но он кажется излишне заумным (не говоря уже о довольно хрупком):
extracted_obj = ObjectSpace._id2ref(
obj_enum.inspect.match(/0x[0-9a-f]*/).values_at(0)[0].to_i(16)/2
)
p obj.equal? extracted_obj # => true
Если это неясно, я проверяю объект Enumerator, используя регулярное выражение для извлечения идентификатора исходного объекта из результирующей строки, преобразования его в целое число (и деления на 2) и использования ObjectSpace._id2ref
для преобразования идентификатора в ссылка на мой объект. Уродливые вещи.
Мне трудно поверить, что это самый простой способ выполнить эту работу, но несколько часов гугления ничего мне не открыли. Есть ли простой способ извлечь объект после обертывания перечислителя вокруг него с помощью #to_enum
, или это в значительной степени способ сделать это?
Изменить:
Как говорит Амадан ниже (и очень признателен, Амадан), это может быть проблема XY, и мне, возможно, придется переосмыслить свое решение. Я немного объясню, как я сюда попал.
Вариант использования (мета): у меня есть (переменное) количество объектов в массиве. Каждый объект предоставляет массив целых чисел (все одинакового размера) в качестве атрибута, отсортированного от большего к меньшему. Я хочу одновременно перебирать массивы каждого из объектов, находя объект или объекты с наибольшим целым числом, не совпадающим в массиве другого объекта.
Казалось, что внешняя итерация была хорошим способом сделать это, так как одновременная внутренняя итерация нескольких объектов, которые должны знать о промежуточных результатах итераций друг друга, также довольно быстро распространяется. Но когда я нашел перечислитель, содержащий объект с массивом с наибольшим значением, мне нужно вернуть объект, который он обертывает.
Там вполне может быть лучший способ, чем использование счетчиков, когда это является требованием. Однако фактический процесс итерации и выбора при их использовании довольно тривиален.
Так. Прикладной вариант использования: количество покерных комбинаций без какой-либо руки лучше, чем старшая карта. Найдите выигрышную руку. «Выигрышная рука» - это рука, в которой старшая карта не соответствует по рангу другой руке. (Масти не имеют значения.) Если все карты совпадают в двух или более руках, верните эти руки в виде массива.
«Минимально воспроизводимый пример»:
class Hand
attr_reader :cards
def initialize(cards)
@cards = cards.sort.reverse
end
def each
@cards.each { |card| yield(card.first) }
end
end
class Poker
def initialize(hands)
@hands = hands.map { |hand| Hand.new(hand) }
end
def high_cards
hand_enums = @hands.map(&:to_enum)
loop do
max_rank = hand_enums.map(&:peek).max
hand_enums.delete_if { |enum| enum.peek != max_rank }
hand_enums.each(&:next)
end
hand_enums.map { |e| from_enum(e).cards }
end
def from_enum(enum)
ObjectSpace._id2ref(
enum.inspect.match(/0x[0-9a-f]*/).values_at(0)[0].to_i(16) / 2
)
end
end
hands = [
[[10, "D"], [3, "C"], [8, "C"], [7, "C"], [9, "D"]],
[[10, "D"], [8, "S"], [7, "S"], [9, "H"], [2, "H"]],
[[9, "C"], [8, "H"], [9, "S"], [4, "C"], [7, "D"]]
]
game = Poker.new(hands)
p game.high_cards # => [[[10, "D"], [9, "D"], [8, "C"], [7, "C"], [3, "C"]]]
Это «работает», но я, конечно, согласен с Амаданом, что это хак. Интересный и поучительный, может быть, но все же халтура. TIA для любых предложений.
Enumerator
каким-либо образом раскрывал обернутый объект, кроме найденного вами хака. Однако даже необходимость сделать это является хаком — вы уверены, что нет другого способа, кроме как получить объект отEnumerator
? Подозрение на проблему XY. - person Amadan   schedule 05.02.2020Enumerator.new { |y| y << 1 << 2 << 3 }
создает превосходный перечислитель, который не привязан к конкретному объекту. - person Stefan   schedule 05.02.20201.step
является еще одним примером. - person Cary Swoveland   schedule 05.02.2020Enumerator::Generator
. (Документация по объектуEnumerator::Generator
кстати состоит из одного слова: Generator, которое так же полезно показывает ссылку на самого себя.) Но я лукавлю. Более того, тот факт, что перечислители могут быть созданы без какой-либо внешней ссылки на объект (т. е. ссылки, которую вы и я можем видеть в нашем коде) на объект, не означает, что это невозможно. И вопрос в том, когда это произойдет, используя#to_enum
для переноса существующего объекта в перечислитель. - person BobRodes   schedule 05.02.2020high_cards
: означает ли это, что если в одной руке окажется старшая карта на первой итерации, то другие руки будут удалены. Если да, то почему вам нужно продолжать зацикливать другие карты одной оставшейся руки? Но что, если старшая карта находится в другой руке, но последней в порядке? - person Fabio   schedule 05.02.2020get_highest_hand(hands)
, и внутри этого метода вы бы перебирали все карты, находящиеся в соответствующих руках? - person BKSpurgeon   schedule 06.02.2020Enumerator::Generator
. Это не коллекция? Я вижу объект Enumerator как оболочку для другого объекта, который выполняет итерацию по внутреннему объекту в соответствии с тем, что этот объект дает для итерации. - person BobRodes   schedule 06.02.2020Enumerator::Generator
кажется оболочкой вокруг блока, переданногоEnumerator.new
, так чтоEnumerator
может иметь дело с объектом, имеющим методeach
. Но это детали реализации. Счетчики не раскрывают свои источники, они просто выдают значения. - person Stefan   schedule 06.02.2020high_cards
? - person BobRodes   schedule 06.02.2020