Спискові вирази
Генераторні списки (англ. list comprehension) — синтаксична конструкція доступна в деяких мовах програмування, яка призначена для створення списків застосуванням операцій над існуючими списками. Вона відповідає математичній нотації побудови множини і замінює використання функцій map та filter.
Опис
Розгляньмо наступний приклад запису множини:
Це можна прочитати як, " множина всіх чисел виду "2 на ", де — елемент з множини натуральних чисел (), такий що в квадраті більше ніж ."
В мові Haskell такий запис множини можна відтворити генераторним списком такого вигляду:
s = [ 2*x | x <- [1..], x^2 > 3 ]
Тут список [1..]
представляє , x^2>3
представляє предикат, а 2*x
- вихідний вираз.
Генераторні списки дають результати в означеному порядку (на відміну від членів множини); і генераторні списки можуть генерувати елементи за потребою, а не одразу ввесь список, дозволяючи таким чином, наприклад попереднє означення членів нескінченної множини.
Історія
Мова програмування SETL (кінець 1960-тих) мала інструкції конструюванння множин, і система комп'ютерної алгебри AXIOM (1973) мала подібні конструкції, які обробляли потоки, але вперше термін "comprehension" для таких конструкцій був використаний Родом Бурсталом та Джоном Дарлінґтоном в описі мови програмування NPL (1977).
Smalltalk block context messages, які являють собою генераторні списки, були в цій мові щонайменше з часів Smalltalk-80.
Робота Бурстала та Дарглінґтона з NPL вплинула на багато мов програмування протягом 1980-тих, але не всі з них включали генераторні списки. Винятком була впливова лінива чисто функціональна мова програмування Miranda, випущена в 1985-тому. Розроблена пізніше, теж цілком функціональна мова з лінивими обчисленнями Haskell, ввібрала багато особливостей Міранди, включно з генераторними списками. Мова програмування Python теж піддалась сильному впливу лінивих функціональних мов і ввела генераторні списки. Чисті функціональні мови залишаються нішевими, в той час як Python став значно популярнішим і представив генераторні списки ширшій аудиторії.
Генераторні списки пропонувались як нотація запитів до баз даних[1] і були реалізовані в мові запитів Kleisli.[2]
Приклади в різних мовах програмування
Далі йтимуть приклади синтаксису генераторних списків в різних мовах програмування:
Хоча в оригінальному прикладі використовується нескінченний список, виразити його можуть не всі мови, тому в деяких ми покажемо як використати підмножину замість підмножини .
C#
var s =
from x in Enumerable.Range(0, 100)
where x * x > 3
select x * 2;
чи
var s = Enumerable.Range(0, 100).Where(x => x*x > 3).Select(x => x*2);
Ceylon
{ for (x in 0..100) if ( x**2 > 3) x * 2 }
Clojure
Clojure генерує нескінченні ліниві послідовності (подібно до Хаскеля, чи генераторів Пайтона). Використовуйте take
щоб отримати перші N результатів нескінченної послідовності.
(take 20 (for [x (iterate inc 0) :when (> (* x x) 3)] (* 2 x)))
CoffeeScript
(x * 2 for x in [0..20] when x*x > 3)
Common Lisp
Генераторні списки можуть виражатись за допомогою ключового слова collect
макроса loop
. Умови виражаються за допомогою if
, як показано нижче:
(loop for x from 0 to 100 if (> (* x x) 3) collect (* 2 x))
Нескінченна лінива послідовність може створюватись різними способами, наприклад за допомогою об'єктів CLOS чи макросом yield.
Erlang
S = [2*X || X <- lists:seq(0,100), X*X > 3].
F#
Генератори мають форму [for x in collection do ... yield expr]
для списків та seq {for x in collection do ... yield expr}
для послідовностей.
> seq { for x in 0..100 do
if x*x > 3 then yield 2*x } ;;
val it : seq<int> = seq [4; 6; 8; 10; ...]
Groovy
Groovy підтримує вирази для таких колекцій в Java як list, set, map.
s = (1..100).grep { it ** 2 > 3 }.collect { it * 2 }
Змінна "it" є скороченням неявного параметри замикання. Наступник код є еквівалентом попереднього
s = (1..100).grep { x -> x ** 2 > 3 }.collect { x -> x * 2 }
Haskell
s = [ 2*x | x <- [0..], x^2 > 3 ]
Perl 6
my @s = ($_ * 2 if $_ ** 2 > 3 for ^100);
чи:
my @s = gather { for ^100 { take 2 * $_ if $_ ** 2 > 3 } };
PowerShell
0..100 | Where {$_ * $_ -gt 3} | ForEach {$_ * 2}
Дивись також
- Оператор SELECT в SQL
- Монади
- Інші конструкції для обробки послідовностей:
- Інші конструкції програмування, перенесені з математичних форм запису:
- Варта
- Зіставлення зі зразком
- Оператор (програмування)
Посилання
- List Comprehension in The Free On-line Dictionary of Computing, Editor Denis Howe.
- Trinder, Phil (1992). Comprehensions, a query notation for DBPLs. Proceedings of the third international workshop on Database programming languages: bulk types & persistent data, Nafplion, Greece. с. 55–68.
- Wadler, Philip (1990). Comprehending Monads. Proceedings of the 1990 ACM Conference on LISP and Functional Programming, Nice.
- Wong, Limsoon (2000). The Functional Guts of the Kleisli Query System. Proceedings of the fifth ACM SIGPLAN international conference on Functional programming International Conference on Functional Programming. с. 1–10.
Haskell
- The Haskell 98 Report, chapter 3.11 List Comprehensions.
- The Glorious Glasgow Haskell Compilation System User's Guide, chapter 7.3.4 Parallel List Comprehensions.
- The Hugs 98 User's Guide, chapter 5.1.2 Parallel list comprehensions (a.k.a. zip-comprehensions).
Python
- Python Reference Manual, chapter 5.2.4 List displays.
- Python Enhancement Proposal PEP 202: List Comprehensions.
- Python Reference Manual, chapter 5.2.5 Generator expressions.
- Python Enhancement Proposal PEP 289: Generator Expressions.
Common Lisp
- Implementation of a Lisp comprehension macro by Guy Lapalme
Axiom
Решта
- SQL-подібні оператори над множинами в Python Cookbook (англ.)
- Обговорення генераторних списків та пов'язаних конструкцій в Scheme (англ.)
- Генераторні списки в різних мовах програмування (англ.)