善用 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 中解析出適合作為該網頁縮圖的圖片, 但有幾個不太舒服的點:
- 因為元素可能不存在, 因此當中使用了很多
try
一一檢查. - 寫成一排太長, 但因為是長敘述換行, 不縮排也很奇怪, 怎樣都不舒服.
現實中也是常有這種 “先嘗試 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') }
)