A понимание списка - это синтаксическая конструкция, доступная в некоторых языках программирования для создание списка на основе существующих списков . Он следует форме математической нотации построителя множеств (понимание множества) в отличие от использования функций map и filter.
Рассмотрим следующий пример в нотации конструктора наборов.
или часто
Это можно прочитать так:" - это набор всех чисел "2 раза "ТАКОЕ, ЧТО является ЭЛЕМЕНТОМ или ЧЛЕНОМ набора натуральных чисел (), И в квадрате больше, чем . "
Наименьшее натуральное число, x = 1, не удовлетворяет условию x>3 (условие 1>3 неверно), поэтому 2 · 1 не входит в S. Следующее натуральное число, 2, соответствует удовлетворяют условию (2>3), как и любое другое натуральное число. Таким образом, x состоит из 2, 3, 4, 5... Так как множество S состоит из всех чисел, умноженных на два, оно задается формулой S = {4, 6, 8, 10,...}. Другими словами, S - это набор всех четных чисел, превышающих 2.
В этой аннотированной версии примера:
Составление списка имеет одинаковые синтаксические компоненты для представления создания списка по порядку из входного списка или итератор :
Порядок создания элементов выходного списка основан на порядке элементов в вход.
В синтаксисе понимания списков Haskell эта конструкция построителя множеств будет записана аналогично, как:
s = [2 * x | x <- [0..], x^2>3]
Здесь список [0..]
представляет , x ^ 2>3
представляет предикат, а 2 * x
представляет выходное выражение.
Составления списков дают результаты в определенном порядке (в отличие от членов наборов); и понимания списков могут генерировать элементы списка по порядку, а не создавать весь список, таким образом позволяя, например, предыдущее определение Haskell членов бесконечного списка.
Существование связанных конструкций предшествовало использованию термина «понимание списка». Язык программирования SETL (1969) имеет конструкцию формирования набора, аналогичную пониманию списков. Например, этот код печатает все простые числа от 2 до N:
print ([n in [2..N] | forall m in {2..n - 1} | n mod m>0]) ;
Система компьютерной алгебры АКСИОМА (1973) имеет аналогичную конструкцию, которая обрабатывает потоки.
Первое использование термина «понимание» для таких конструкции были в Rod Burstall и описание их функционального языка программирования NPL с 1977 года. В его ретроспективе «Some History of Functional Programming Languages» Дэвид Тернер напоминает:
NPL был реализован в POP2 Берстоллом и использовался в работе Дарлингтона по преобразованию программ (Burstall Darlington 1977). Это был язык первого порядка, строго (но не полиморфно) типизированный, чисто функциональный, с вызовом по значению. В нем также были «устойчивые выражения», например
setofeven (X) <= <:x : x in X even(x):>}}
В сноске, приложенной к термину «понимание списка», Тернер также отмечает
Я изначально назвал эти выражения ZF ссылкой на теорию множеств Цермело-Франкеля - это было Фил Уодлер, придумавший лучшее понимание списка терминов.
Работа Берстолла и Дарлингтона с NPL повлияла на многие языки функционального программирования в течение 1980-х годов, но не все включали понимание списков. Исключением был влиятельный чистый, ленивый функциональный язык программирования Тернера Miranda, выпущенный в 1985 году. Разработанный впоследствии стандартный чистый ленивый функциональный язык Haskell включает в себя многие функции Миранды, включая понимание списков.
Компоненты были предложены как нотация запросов для баз данных и реализованы на языке запросов к базам данных Kleisli.
В Haskell понимание монад является обобщением понимания списка на другие монады в функциональном программировании.
Версии 3.x и 2.7 языка Python вводят синтаксис для интерпретаций set. По форме похожие на составления списков, понимания множеств генерируют наборы Python вместо списков.
>>>s = {v вместо v в 'ABCDABCD', если v не в 'CB'}>>>print (s) {'A', 'D'}>>>тип (ы)>>>
Racket set computing создает наборы Racket вместо списков.
(для / set ([v "ABCDABCD"] #: if (member v (string->list "CB"))) v))
Версия 3.x и 2.7 языка Python представил новый синтаксис для понимания словаря, похожий по форме на понимание списков, но который генерирует Python dicts вместо списков.
>>>s = {ключ: val для ключа, val в перечислении ('ABCD'), если val не в 'CB'}>>>s {0: 'A', 3: 'D'}>>>
Компоненты хэш-таблицы Racket генерируют хеш-таблицы Racket (одна реализация типа словаря Racket).
(для / hash ([(ключ val) (индексированный "ABCD")] #: if (member val (string->list "CB"))) (values key val))
Компилятор Glasgow Haskell имеет расширение, называемое понимание параллельного списка (также известное как zip-понимание ), которое позволяет использовать несколько независимых ветвей квалификаторы в синтаксисе понимания списка. В то время как квалификаторы, разделенные запятыми, являются зависимыми («вложенными»), ветви квалификаторов, разделенные вертикальными линиями, оцениваются параллельно (это не относится ни к какой форме многопоточности: это просто означает, что ветви заархивированы ).
- понимание обычного списка a = [(x, y) | x <- [1..5], y <- [3..5]] -- [(1,3),(1,4),(1,5),(2,3),(2,4)... -- zipped list comprehension b = [(x,y) | (x,y) <- zip [1..5] [3..5]] -- [(1,3),(2,4),(3,5)] -- parallel list comprehension c = [(x,y) | x <- [1..5] | y <- [3..5]] -- [(1,3),(2,4),(3,5)]
Стандартная библиотека представлений Racket содержит параллельные и вложенные версии его пониманий, которые отличаются в названии "for" vs "for *". Например, векторные представления «for / vector» и «for * / vector» создают векторы путем параллельного или вложенного итерации по последовательностям. Ниже приведен код Racket для примеров понимания списка Haskell.
>(для * / list ([x (в диапазоне 1 6)] [y (в диапазоне 3 6)]) (список xy)) '((1 3) (1 4) (1 5) (2 3) (2 4) (2 5) (3 3) (3 4) (3 5) (4 3) (4 4) (4 5) (5 3) (5 4) (5 5))>(for / list ([x (in-range 1 6)] [y (in-range 3 6)]) (list xy)) '((1 3) (2 4) (3 5))
In Python, мы могли бы сделать следующее:
# понимание обычного списка>>>a = [(x, y) для x в диапазоне (1, 6) для y в диапазоне (3, 6)] [(1, 3), (1, 4), (1, 5), (2, 3), (2, 4),... # понимание параллельных / сжатых списков>>>b = [x for x in zip (range (1, 6), range (3, 6))] [(1, 3), (2, 4), (3, 5)]
В Юлии практически тех же результатов можно добиться следующим образом:
# понимание обычного массива>>>a = [(x, y) для x в 1: 5 для y в 3: 5] # понимание параллельного / заархивированного массива>>>b = [x для x в zip (1: 3, 3: 5)]
с той лишь разницей, что вместо списков в Julia у нас есть массивы.
Подобно исходному использованию NPL, это в основном языки доступа к базе данных.
Это делает концепцию понимания более важной, потому что с вычислительной точки зрения невозможно получить весь список и работать с ним (исходный «весь список» может быть всей базой данных XML).
В XPath выражение:
/ library / book // paragraph [@ style = 'first-in-chapter']
концептуально оценивается как серия «шагов», где каждый шаг создает список, и следующий шаг применяет функцию фильтра к каждому элементу в выходных данных предыдущего шага.
В XQuery доступен полный XPath, но также используются операторы FLWOR, которые являются более мощная конструкция понимания.
для $ b в // книге, где $ b [@pages < 400] order by $b//title return{$b//title} {($ book // параграф) [1 ]}
Здесь XPath // книга оценивается для создания последовательности (также известной как список); предложение where является функциональным «фильтром», результат сортируется по порядку, а фрагмент XML
на самом деле является анонимной функцией, которая создает / преобразует XML для каждого элемента в последовательность, использующая подход «карты», найденный в других функциональных языках.
Итак, на другом функциональном языке указанная выше инструкция FLWOR может быть реализована следующим образом:
map (newXML (shortBook, newXML (title, $ 1.title), newXML (firstPara, $ 1...)) filter (lt ($ 1.pages, 400), xpath (// book)))
C# 3.0 имеет группу связанных функций под названием LINQ, которая определяет набор запросов операторы для управления перечислениями объектов.
var s = Enumerable.Range (0, 100).Where (x =>x * x>3).Select (x =>x * 2);
Он также предлагает альтернативный синтаксис понимания, напоминающий SQL:
var s = from x in Enumerable.Range (0, 100), где x * x>3 select x * 2;
LINQ предоставляет возможности по сравнению с типичными реализациями List Computing. Когда корневой объект понимания реализует интерфейс IQueryable, а не просто выполняет связанные методы понимания, вся последовательность команд преобразуется в объект абстрактного синтаксического дерева (AST), который передается в объект IQueryable для интерпретации и выполнения.
Это позволяет, среди прочего, для IQueryable
C ++ не имеет каких-либо языковых функций, напрямую поддерживающих понимание списков, но перегрузка оператора (например, перегрузка |,>>,>>=) успешно использовалась для обеспечения выразительного синтаксиса для "встроенного" запрос DSL. В качестве альтернативы, списки могут быть построены с использованием идиомы erase-remove для выбора элементов в контейнере и алгоритма STL for_each для их преобразования.
#include#include #include
с использованием пространства имен std; template C comprehend (C source, const P predicate, const T transformation) {// инициализируем назначение C d = forward (source); // фильтруем элементы d.erase (remove_if (begin (d), end (d), predicate), end (d)); // применяем преобразование for_each (begin (d), end (d), transformation); return d; } int main () {список диапазон (10); // диапазон - это список из 10 элементов, все ноль йоты (begin (range), end (range), 1); // диапазон теперь содержит 1,2,..., 10 list result = comprehend (range, (int x) {return x * x <= 3;}, (int x){x *= 2;}); // result now contains 4,6,...,20 }
Есть некоторые усилия по обеспечению C ++ конструкциями / синтаксисом для понимания списка аналогично обозначению конструктора наборов.
counting_range (1,10) | filter (_1 * _1>3) | transformed (ret(_1 * 2))
listN; list S; for (int i = 0; i < 10; i++) N.push_back(i); S << list_comprehension(3.1415 * x, x, N, x*x>3)
bool even (int x) {return x% 2 == 0;} bool x2 (int x) {х * = 2; вернуть истину; } списокl, t; int x, y; for (int i = 0; i < 10; i++) l.push_back(i); (x, t) = l | x2; (t, y) = t; t = l < 9; t = t < 7 | even | x2;
Гамлет 9.99 Уильям Шекспир Англия ... ...
LEESA предоставляет>>для разделителя / X-Path. Разделитель X-Path //, который «пропускает» промежуточные узлы в дереве, реализован в LEESA с использованием так называемого стратегического программирования. В приведенном ниже примере catalog_, book_, author_ и name_ - экземпляры классов catalog, book, author и name соответственно.
// Эквивалентный X-Path: "catalog / book / author / name" std :: vectorauthor_names = оценка (корень, каталог_>>книга_>>автор_>>имя_); // Эквивалентный X-Path: "каталог // имя" std :: vector author_names = Assessment (root, catalog_>>DescendantsOf (catalog_, name_)); // Эквивалентный X-Path: "catalog // author [country ==" England "]" std :: vector author_names = Assessment (root, catalog_>>DescendantsOf (catalog_, author_)>>Select (author_, ( const author a) {return a.country () == "England";})>>name_);