Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Tạo Enumerator Ruby Động: Hướng Dẫn Nhanh Cho Việc Tạo Tại Chỗ

Khi bạn xử lý các tập hợp như enumerators, bạn có thể sử dụng tất cả các phương thức yêu thích như #map và #reduce mà không cần viết mã thêm. Thật tuyệt vời!

Ngày xưa, việc định nghĩa enumerators hơi phiền toái. Bạn phải tạo lớp mới, include module Enumerable, và định nghĩa phương thức #each.

Tuy nhiên, kể từ Ruby 1.9, chúng ta có cách nhẹ nhàng hơn để định nghĩa enumerators ngay tại chỗ. Hãy cùng tìm hiểu.

Giới thiệu lớp Enumerator

Lớp Enumerator cho phép bạn định nghĩa một enumerator dùng một lần, bằng cách sử dụng cú pháp block. Trong ví dụ dưới đây, chúng ta tạo một enumerator trả về một chuỗi vô hạn các số ngẫu nhiên.

e = Enumerator.new do |y|
  loop do
    y << rand(10) # Toán tử << "yield" một giá trị
  end
end
# Cho enumerator "yield" 10 giá trị, sau đó dừng
puts e.first(10).inspect # => [6, 6, 7, 2, 2, 9, 6, 8, 2, 1]

Bạn có thể nhận thấy chúng ta đang sử dụng toán tử shift << theo cách lạ. Đây là viết tắt cho phương thức y.yield. Bạn sẽ gọi nó cho mỗi mục trong enumerator. Nếu tất cả điều này hơi kỳ diệu, đừng lo lắng. Nó thực sự là vậy.

Kích thước tập hợp

Xác định kích thước của một tập hợp đặt ra vấn đề cho enumerators lười biếng của Ruby. Để đếm số mục trong tập hợp, bạn phải tải toàn bộ tập hợp - điều này đi ngược lại toàn bộ ý nghĩa của việc sử dụng enumerators lười biếng.

Có một giải pháp thay thế, kiểu gì. Nếu bạn biết trước kích thước của tập hợp tại thời điểm tạo Enumerator, bạn có thể cung cấp nó.

# Bạn có thể truyền độ dài làm tham số cho constructor, nếu bạn có nó
e = Enumerator.new(10) do |y|
  10.times { y << rand }
end

Ví dụ thực tế của tôi

Chính hôm qua tôi đang làm việc trên trang tài liệu mới của Honeybadger. Nó được xây dựng bằng Jekyll, và tôi đang viết một plugin để tạo mục lục dựa trên các thẻ <h2> và <h3> trong tài liệu.

Việc xác định các thẻ <h3> nào thuộc về phần do thẻ <h2> định nghĩa hơi khó khăn. Bạn phải parse HTML bằng nokogiri, sau đó quét tài liệu kết quả. Vì vậy tôi tách riêng đoạn mã đó và biến nó thành một Enumerator. Dưới đây là giao diện của nó.

def subheadings(el)
  Enumerator.new do |y|
    next_el = el.next_sibling
    while next_el && next_el.name != "h2"
      if next_el.name == "h3"
        y << next_el
      end
      next_el = next_el.next_sibling
    end
  end
end