善用 lazy enumerator 除了可以增進效能以外, 還能夠讓程式更容易閱讀, 像是以下這個例子:

response = Faraday.get(url)
body = Oga.parse_html(response.body)

body.css('meta[property="og:image"]').first.try(:get, 'content') ||
  body.css('link[rel="image_src"]').first.try(:get, 'href') ||
  body.css('img').first.try(:get, 'src')

這段程式碼嘗試從 html 中解析出適合作為該網頁縮圖的圖片, 但有幾個不太舒服的點:

  1. 因為元素可能不存在, 因此當中使用了很多 try 一一檢查.
  2. 寫成一排太長, 但因為是長敘述換行, 不縮排也很奇怪, 怎樣都不舒服.

現實中也是常有這種 “先嘗試 A, 不行的話做 B, 還是不行的話做 C”, 如果 A, B, C 很複雜的話, 通常會寫成 method, 大概像這樣:

find_image_from_og(body) || find_image_from_rel(body) || find_image_from_img_tag(body)

但這裡也可以使用 lazy enumerator 來做這件事情, 寫起來像這樣:

def try_blocks(*blocks)
  blocks.lazy.map { |block| block.call rescue nil }.reject(&:blank?).first
end

try_blocks(
  -> { body.css('meta[property="og:image"]').first.get('content') },
  -> { body.css('link[rel="image_src"]').first.get('href') },
  -> { body.css('img').first.get('src') }
)