Донс дав дуже добру відповідь, але він пропустив, що є (для мене) однією з найпереконливіших особливостей ітераторів: вони полегшують міркування щодо управління космосом, оскільки старі дані повинні бути явно збережені. Розглянемо:
average :: [Float] -> Float
average xs = sum xs / length xs
Це добре відомий витік простору, оскільки весь список xs
повинен зберігатися в пам'яті, щоб обчислити sum
і length
. Можна зробити ефективним споживачем, створивши складку:
average2 :: [Float] -> Float
average2 xs = uncurry (/) <$> foldl (\(sumT, n) x -> (sumT+x, n+1)) (0,0) xs
Але дещо незручно робити це для кожного потокового процесора. Є деякі узагальнення ( Conal Elliott - Beautiful Fold Zipping ), але вони, схоже, не встигли. Однак ітератори можуть отримати подібний рівень висловлювання.
aveIter = uncurry (/) <$> I.zip I.sum I.length
Це не так ефективно, як згин, оскільки список все ще повторюється кілька разів, однак він збирається у шматки, тому старі дані можна ефективно збирати сміттям. Для того, щоб зламати цю властивість, необхідно явно зберегти весь вхід, наприклад, за допомогою stream2list:
badAveIter = (\xs -> sum xs / length xs) <$> I.stream2list
Стан ітерацій як моделі програмування знаходиться в стадії розробки, проте це набагато краще, ніж навіть рік тому. Ми вчимося , що комбінатори корисні (наприклад zip
, breakE
, enumWith
) , і які в меншій мірі, в результаті чого вбудований iteratees і комбінатори забезпечують постійно більше виразності.
Тим не менш, Донс правильно, що вони є передовою технікою; Я б, звичайно, не використовував їх для кожної проблеми вводу-виводу.