Jump to content

Понимание списка

Понимание списка это синтаксическая конструкция, доступная в некоторых языках программирования для создания списка на основе существующих списков . Он следует форме математической нотации построителя множеств ( понимание множеств ), в отличие от использования функций карты и фильтра .

Обзор [ править ]

Рассмотрим следующий пример в математической нотации построителя множеств .

или часто

Это можно прочитать» это набор всех чисел "2 раза " ТАКОЕ ТАКОЕ является ЭЛЕМЕНТОМ или ЧЛЕНОМ множества натуральных чисел ( ), И в квадрате больше, чем ."

Наименьшее натуральное число x = 1 не удовлетворяет условию x 2 >3 (условие 1 2 >3 неверно), поэтому 2 · 1 не входит в S. Следующее натуральное число, 2, действительно удовлетворяет условию (2 2 >3), как и любое другое натуральное число. Таким образом, x состоит из 2, 3, 4, 5... Поскольку множество S состоит из всех чисел «2 раза x», оно задается формулой S = {4, 6, 8, 10,...}. Другими словами, S — это набор всех четных чисел, больших 2.

В этой аннотированной версии примера:

  • — это переменная, представляющая элементы входного набора.
  • представляет входной набор, который в этом примере представляет собой набор натуральных чисел
  • — это выражение предиката, действующее как фильтр для членов входного набора.
  • — это выходное выражение, создающее элементы нового набора из элементов входного набора, которые удовлетворяют выражению предиката.
  • фигурные скобки указывают, что результатом является набор
  • вертикальная черта читается как «ТАКОЕ ЭТО». Черта и двоеточие «:» взаимозаменяемы.
  • запятые разделяют предикаты и могут читаться как «И».

Понимание списка имеет те же синтаксические компоненты, которые представляют генерацию списка по порядку из входного списка или итератора :

  • Переменная, представляющая элементы входного списка.
  • Список ввода (или итератор).
  • Необязательное выражение предиката.
  • И выходное выражение, создающее элементы выходного списка из элементов входной итерации, удовлетворяющих предикату.

Порядок генерации членов выходного списка основан на порядке элементов во входных данных.

В синтаксисе распознавания списков Haskell эта конструкция set-builder будет записана аналогично:

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]);

Система компьютерной алгебры AXIOM (1973) имеет аналогичную конструкцию, обрабатывающую потоки .

Впервые термин «понимание» для таких конструкций был использован в Родом Берстоллом и Джоном Дарлингтоном описании их функционального языка программирования NPL в 1977 году. В его ретроспективе «Некоторые истории языков функционального программирования» [1] Дэвид Тернер вспоминает:

NPL был реализован в POP2 Берстоллом и использован Дарлингтоном в работе по преобразованию программ (Burstall & Darlington 1977). Язык был первого порядка, строго (но не полиморфно) типизированным, чисто функциональным, с вызовом по значению. У него также были «устоявшиеся выражения», например

setofeven (X)  <=  <:x : x in X & even(x):>}}

В сноске к термину «понимание списка» Тернер также отмечает:

Первоначально я назвал эти выражения ZF , отсылая к теории множеств Цермело-Френкеля — именно Фил Уодлер придумал лучший термин «понимание списка» .

Работа Берстолла и Дарлингтона с NPL повлияла на многие языки функционального программирования в 1980-е годы, но не все включали в себя понимание списков. Исключением стал влиятельный, чистый, ленивый, функциональный язык программирования Тернера Miranda , выпущенный в 1985 году. Разработанный впоследствии стандартный чисто ленивый функциональный язык Haskell включает в себя многие функции Миранды, включая понимание списков.

Понимания были предложены в качестве нотации запроса для баз данных. [2] и были реализованы на языке запросов к базе данных Клейсли . [3]

Примеры на разных языках программирования [ править ]

Подобные конструкции [ править ]

Понимание монады [ править ]

В Haskell понимание монад — это обобщение понимания списков на другие монады функционального программирования .

Установить понимание [ править ]

Язык Python вводит синтаксис для понимания множеств , начиная с версии 2.7. Подобно генераторам списков, генераторы наборов генерируют наборы Python вместо списков.

>>> s = {v for v in 'ABCDABCD' if v not in 'CB'}
>>> print(s)
{'A', 'D'}
>>> type(s)
<class 'set'>
>>>

Понимания наборов ракеток генерируют наборы ракеток вместо списков.

(for/set ([v "ABCDABCD"] #:unless (member v (string->list "CB")))
         v))

Понимание словаря [ править ]

В языке Python появился новый синтаксис для понимания словаря в версии 2.7 , похожий по форме на понимание списков, но который генерирует словари Python вместо списков.

>>> s = {key: val for key, val in enumerate('ABCD') if val not in 'CB'}
>>> s
{0: 'A', 3: 'D'}
>>>

Понимания хеш-таблиц Racket генерируют хеш-таблицы Racket (одна реализация типа словаря Racket).

(for/hash ([(val key) (in-indexed "ABCD")]
           #:unless (member val (string->list "CB")))
  (values key val))

Понимание параллельного списка [ править ]

Компилятор Glasgow Haskell имеет расширение, называемое параллельным пониманием списка (также известное как zip-comrehension ), которое позволяет использовать несколько независимых ветвей квалификаторов в синтаксисе понимания списка. В то время как квалификаторы, разделенные запятыми, являются зависимыми («вложенными»), ветви квалификаторов, разделенные вертикальной чертой, оцениваются параллельно (это не относится к какой-либо форме многопоточности: это просто означает, что ветви заархивированы ) .

-- regular list comprehension
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» и «for*». Например, векторные выражения «for/vector» и «for*/vector» создают векторы путем параллельной, а не вложенной итерации над последовательностями. Ниже приведен код Racket для примеров понимания списков Haskell.

> (for*/list ([x (in-range 1 6)] [y (in-range 3 6)]) (list x y))
'((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 x y))
'((1 3) (2 4) (3 5))

В Python мы могли бы сделать следующее:

>>> # regular list comprehension
>>> a = [(x, y) for x in range(1, 6) for y in range(3, 6)]
[(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), ...
>>>
>>> # parallel/zipped list comprehension
>>> b = [x for x in zip(range(1, 6), range(3, 6))]
[(1, 3), (2, 4), (3, 5)]

В Юлии практически тех же результатов можно добиться следующим образом:

# regular array comprehension
>>> a = [(x, y) for x in 1:5 for y in 3:5]

# parallel/zipped array comprehension
>>> b = [x for x in zip(1:3, 3:5)]

с той лишь разницей, что вместо списков в Джулии у нас массивы.

XQuery и XPath [ править ]

Как и исходное использование NPL, это по сути языки доступа к базе данных.

Это делает концепцию понимания более важной, поскольку с вычислительной точки зрения невозможно получить весь список и работать с ним (исходный «весь список» может представлять собой всю базу данных XML).

В XPath выражение:

/library/book//paragraph[@style='first-in-chapter']

концептуально оценивается как серия «шагов», где каждый шаг создает список, а следующий шаг применяет функцию фильтра к каждому элементу в выходных данных предыдущего шага. [4]

В XQuery доступен полный XPath, но FLWOR , которые являются более мощной конструкцией понимания. также используются операторы [5]

for $b in //book
where $b[@pages < 400]
order by $b//title
return
  <shortBook>
    <title>{$b//title}</title>
    <firstPara>{($book//paragraph)[1]}</firstPara>
  </shortBook>

Здесь XPath //book оценивается для создания последовательности (также известной как список); предложениеwhere является функциональным «фильтром», порядок сортирует результат, а <shortBook>...</shortBook> Фрагмент XML на самом деле представляет собой анонимную функцию , которая создает/преобразует XML для каждого элемента последовательности, используя подход «карты», используемый в других функциональных языках.

Итак, на другом функциональном языке приведенный выше оператор FLWOR может быть реализован следующим образом:

map(
  newXML(shortBook, newXML(title, $1.title), newXML(firstPara, $1...))
  filter(
    lt($1.pages, 400),
    xpath(//book)
  )
)

LINQ в C# [ править ]

В 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) where x * x > 3 select x * 2;

LINQ предоставляет возможности по сравнению с обычными реализациями понимания списков. Когда корневой объект понимания реализует IQueryable Интерфейс, а не просто выполнение связанных методов понимания, вся последовательность команд преобразуется в объект абстрактного синтаксического дерева (AST), который передается объекту IQueryable для интерпретации и выполнения.

Это позволяет, среди прочего, IQueryable

  • переписать несовместимое или неэффективное понимание
  • перевести AST на другой язык запросов (например, SQL) для выполнения

С++ [ править ]

C++ не имеет каких-либо языковых функций, непосредственно поддерживающих понимание списков, но есть перегрузка операторов (например, перегрузка |, >>, >>=) успешно использовался для обеспечения выразительного синтаксиса для «встроенных» языков запросов, специфичных для предметной области (DSL). Альтернативно, списки могут быть созданы с использованием идиомы стирания-удаления для выбора элементов в контейнере и алгоритма STL for_each для их преобразования.

#include <algorithm>
#include <list>
#include <numeric>

using namespace std;

template<class C, class P, class T>
C comprehend(C&& source, const P& predicate, const T& transformation)
{
  // initialize destination
  C d = forward<C>(source);

  // filter elements
  d.erase(remove_if(begin(d), end(d), predicate), end(d));

  // apply transformation
  for_each(begin(d), end(d), transformation);

  return d;
}

int main()
{
  list<int> range(10);
      // range is a list of 10 elements, all zero
  iota(begin(range), end(range), 1);
      // range now contains 1, 2, ..., 10

  list<int> result = comprehend(
      range,
      [](int x) { return x * x <= 3; },
      [](int &x) { x *= 2; });
      // result now contains 4, 6, ..., 20
}

Предпринимаются некоторые усилия по обеспечению C++ конструкциями/синтаксисом понимания списков, аналогичными нотации построителя множеств.

  • В Бусте . В библиотеке Range [1] существует понятие адаптеров [2] , которые можно применять к любому диапазону и выполнять фильтрацию, преобразование и т. д. С этой библиотекой исходный пример Haskell будет выглядеть так (с использованием Boost.Lambda [3] для анонимной фильтрации и функции преобразования) ( Полный пример ):
    counting_range(1,10) | filtered( _1*_1 > 3 ) | transformed(ret<int>( _1*2 ))
    
  • Этот [6] реализация использует макрос и перегружает оператор <<. Он оценивает любое выражение, допустимое внутри «if», и может быть выбрано любое имя переменной. это не потокобезопасно Однако . Пример использования:
list<int> N;
list<double> S;

for (int i = 0; i < 10; i++)
    N.push_back(i);

S << list_comprehension(3.1415 * x, x, N, x * x > 3)
  • Этот [7] реализация обеспечивает разделение головы и хвоста с использованием классов и перегрузки операторов, а | оператор фильтрации списков (с использованием функций). Пример использования:
bool even(int x) { return x % 2 == 0; }
bool x2(int &x) { x *= 2; return true; }

list<int> 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;
  • Язык встроенных запросов и обхода (LEESA [8] ) — это встроенный DSL в C++, который реализует запросы типа X-Path с использованием перегрузки операторов. Запросы выполняются на широко типизированных деревьях XML, полученных с использованием привязки xml к C++ из XSD. Строкового кодирования абсолютно нет. Даже имена тегов xml являются классами, поэтому опечатки исключены. Если выражение LEESA образует неправильный путь, которого нет в модели данных, компилятор C++ отклонит код.
    Рассмотрим XML-каталог.
<catalog>
  <book>
    <title>Hamlet</title>
    <price>9.99</price>
    <author>
      <name>William Shakespeare</name>
      <country>England</country>
    </author>
  </book>
  <book>...</book>
...
</catalog>

LEESA предоставляет >> для XPath/разделителя. // Разделитель XPath, который «пропускает» промежуточные узлы в дереве, реализован в LEESA с использованием так называемого стратегического программирования. В приведенном ниже примере каталог_, книга_, автор_ и имя_ являются экземплярами классов каталога, книги, автора и имени соответственно.

// Equivalent X-Path: "catalog/book/author/name"
std::vector<name> author_names = 
evaluate(root, catalog_ >> book_ >> author_ >> name_);

// Equivalent X-Path: "catalog//name"
std::vector<name> author_names = 
evaluate(root, catalog_ >> DescendantsOf(catalog_, name_));

// Equivalent X-Path: "catalog//author[country=="England"]"
std::vector<name> author_names = 
evaluate(root, catalog_  >> DescendantsOf(catalog_, author_)
                         >> Select(author_, [](const author & a) { return a.country() == "England"; })
                         >> name_);

См. также [ править ]

Примечания и ссылки [ править ]

  1. ^ Тернер, Дэвид (2012). «Немного истории функциональных языков программирования» (PDF) . Международный симпозиум по тенденциям функционального программирования, Springer, Берлин, Гейдельберг . стр. 1–20.
  2. ^ Понимание, обозначение запроса для DBPL.
  3. ^ Функциональные возможности системы запросов Клейсли.
  4. ^ «2.1 Этапы определения местоположения» . Язык путей XML (XPath) . W3C . 16 ноября 1999 года. Архивировано из оригинала 9 декабря 2012 года . Проверено 24 декабря 2008 г.
  5. ^ «Выражения XQuery FLWOR» . W3Школы . Архивировано из оригинала 8 октября 2011 г.
  6. ^ «Понимание списков с одной переменной в C++ с использованием макросов препроцессора» . Архивировано из оригинала 21 августа 2011 г. Проверено 9 января 2011 г.
  7. ^ «Понимание списков C++» . Архивировано из оригинала 7 июля 2017 г. Проверено 9 января 2011 г.
  8. ^ «Язык встроенных запросов и обхода (LEESA)» .
  • Понимание списков в Бесплатном онлайн-словаре по информатике, редактор Денис Хоу.
  • Уодлер, Филип (1990). «Понимание монад» . Материалы конференции ACM 1990 года по LISP и функциональному программированию, Ницца .

Внешние ссылки [ править ]

Аксиома [ править ]

Кложур [ править ]

Общий Лисп [ править ]

Хаскелл [ править ]

OCaml [ править ]

Питон [ править ]

Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 7ddfd06eaae39b4f05287987d61c8445__1718246760
URL1:https://arc.ask3.ru/arc/aa/7d/45/7ddfd06eaae39b4f05287987d61c8445.html
Заголовок, (Title) документа по адресу, URL1:
List comprehension - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)