Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

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

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

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

или часто

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

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

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

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

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

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

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

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

s  =  [  2 * x  |  x  <-  [ 0 .. ],  x ^ 2  >  3  ]

Здесь список [0..]представляет , представляет предикат и представляет собой выходное выражение.x^2>32*x

Составления списков дают результаты в определенном порядке (в отличие от членов наборов); и понимание списков может генерировать элементы списка по порядку, а не создавать весь список, таким образом, позволяя, например, предыдущее определение Haskell членов бесконечного списка.

История [ править ]

Существование связанных конструкций предшествовало использованию термина «понимание списка». Язык программирования SETL (1969) имеет конструкцию формирования набора, аналогичную пониманию списков. Например, этот код печатает все простые числа от 2 до N :

print ([n в [2..N] | для всех m в {2..n - 1} | n mod m> 0]);

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

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

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

setofeven (X) <= <: x: x в X и даже (x):>}}

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

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

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

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

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

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

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

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

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

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

>>>  сек  =  { V  для  V  в  'ABCDABCD' ,  если  v  не  в  'СВ' } >>>  печать ( ы ) { 'А' ,  'D' } >>>  тип ( ы ) < класс  ' множества '> >>>

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

( для / set  ([ v  "ABCDABCD" ]  # :  if ( member v  ( string-> list "CB" )))  v ))

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

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

>>>  s  =  { key :  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 Compiler имеет расширение , называемое параллельное список понимание (также известный как почтовый-понимания ) , которая позволяет несколько независимых ветвей классификаторов в синтаксисе списка понимания. В то время как квалификаторы, разделенные запятыми, являются зависимыми («вложенными»), ветви квалификаторов, разделенные вертикальными линиями, оцениваются параллельно (это не относится ни к какой форме многопоточности: это просто означает, что ветки заархивированы ).

- составление регулярного списка a  =  [( x , y )  |  x  <-  [ 1 .. 5 ],  y  <-  [ 3 .. 5 ]] - [(1,3), (1,4), (1,5), (2,3), (2, 4) ...- понимание заархивированного списка b  =  [( x , y )  |  ( x , y )  <-  zip  [ 1 .. 5 ]  [ 3 .. 5 ]] - [(1,3), (2,4), (3,5)]- понимание параллельного списка 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.

>  ( 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 )) >  ( для / list  ([ x  ( в диапазоне  1  6 )]  [ y  ( в диапазоне  3  6 ) ])  ( список x  y )) ' (( 1  3 )  ( 2  4 )  ( 3  5 ))

В 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  for  x  in  zip ( 1 : 3 ,  3 : 5 )]

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

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

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

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

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

/ library / book // параграф [ @style = 'first-in-chapter' ]

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

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

для  $ b  в  // книге, где  $ b [ @pages  <  400 ] упорядочивается по  $ b // заголовок return  <shortBook> <title> { $ b // название } </title> <firstPara> {( $ book // абзац ) [ 1 ]} </firstPara> </shortBook>

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

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

map (  newXML ( shortBook ,  newXML ( заголовок ,  $ 1. заголовок ),  newXML ( firstPara ,  $ 1 ...))  filter (  lt ( $ 1. pages ,  400 ),  xpath (// книга )  ) )

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

В C # 3.0 есть группа связанных функций под названием LINQ , которая определяет набор операторов запроса для управления перечислениями объектов.

var  s  =  Перечислимый . Диапазон ( 0 ,  100 ). Где ( x  =>  x  *  x  >  3 ). Выберите ( x  =>  x  *  2 );

Он также предлагает альтернативный синтаксис понимания, напоминающий SQL:

var  s  =  from  x  в  Enumerable . Диапазон ( 0 ,  100 ),  где  x  *  x  >  3  выберите  x  *  2 ;

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

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

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

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

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

#include  <алгоритм>#include  <список>#include  <число>используя  пространство имен  std ;template < class  C ,  class  P ,  class  T > C  comprehend ( C &&  source ,  const  P &  predicate ,  const  T &  transformation ) {  // инициализируем назначение  C  d  =  forward < C > ( source ); // фильтрующие элементы  d . Стирание ( remove_if ( начинают ( д ),  конец ( д ),  предикат ),  конец ( д )); // применяем преобразование  for_each ( begin ( d ),  end ( d ),  transformation ); return  d ; }Int  Основной () {  список < INT >  Диапазон ( 10 );  // диапазон - это список из 10 элементов, все ноль  йоты ( begin ( range ),  end ( range ),  1 );  // диапазон теперь содержит 1, 2, ..., 10 список < int >  result  =  comprehend (  диапазон ,  [] ( int  x )  {  return  x  *  x  <=  3 ;  },  [] ( int  & x )  {  x  * =  2 ;  });  // результат теперь содержит 4, 6, ..., 20 }

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

  • In Boost . В библиотеке Range [1] есть понятие адаптеров [2], которые могут применяться к любому диапазону и выполнять фильтрацию, преобразование и т. Д. С этой библиотекой исходный пример Haskell будет выглядеть так (с использованием Boost.Lambda [3] для анонимной фильтрации и функции преобразования) ( Полный пример ):
    counting_range ( 1 , 10 )  |  отфильтровано (  _1 * _1  >  3  )  |  преобразованный ( ret < int > (  _1 * 2  ))
  • Эта реализация [6] использует макрос и перегружает оператор <<. Он оценивает любое выражение, допустимое внутри 'if', и может быть выбрано любое имя переменной. Однако это небезопасно . Пример использования:
список < int >  N ; список < двойной >  S ;для  ( INT  я  =  0 ;  я  <  10 ;  я ++ )  Н . push_back ( я );S  <<  list_comprehension ( 3,1415  *  x ,  x ,  N ,  x  *  x  >  3 )
  • Эта [7] реализация обеспечивает нарезку головы / хвоста с использованием классов и перегрузки операторов, а | оператор для фильтрации списков (с помощью функций). Пример использования:
bool  even ( int  x )  {  return  x  %  2  ==  0 ;  } bool  x2 ( int  & x )  {  х  * =  2 ;  вернуть  истину ;  }список < int >  l ,  t ; int  x ,  y ;для  ( int  i  =  0 ;  i  <  10 ;  i ++ )  l . push_back ( я );( x ,  t )  =  l  |  х2 ; ( t ,  y )  =  t ;t  =  l  <  9 ; t  =  t  <  7  |  даже  |  х2 ;
  • Language for Embedded Query and Traversal (LEESA [8] ) - это встроенный DSL в C ++, который реализует запросы, подобные X-Path, с использованием перегрузки операторов. Запросы выполняются на XML-деревьях с богатой типизацией, полученных с помощью привязки XML-C ++ из XSD. Нет абсолютно никакой строковой кодировки. Даже имена тегов xml являются классами, поэтому опечатки недопустимы. Если выражение LEESA формирует неправильный путь, которого нет в модели данных, компилятор C ++ отклонит код.
    Рассмотрим каталог xml.
<catalog>  <book>  <title> Гамлет </title>  <price> 9,99 </price>  <author>  <name> Уильям Шекспир </name>  <country> Англия </country>  </author>  </book>  <book> ... </book>...</catalog>

LEESA предоставляет >>разделитель XPath /. Разделитель // XPath, который «пропускает» промежуточные узлы в дереве, реализован в LEESA с использованием так называемого стратегического программирования. В приведенном ниже примере catalog_, book_, author_ и name_ являются экземплярами классов catalog, book, author и name соответственно.

// Эквивалентный X-Path: "каталог / книга / автор / имя" std :: vector < имя >  author_names  =  оценить ( root ,  catalog_  >>  book_  >>  author_  >>  name_ );// Эквивалент X-Path: "Каталог // имя" станд :: вектор < имя >  AUTHOR_NAMES  =  оценка ( корень ,  catalog_  >>  DescendantsOf ( catalog_ ,  name_ ));// Эквивалент X-Path: "Каталог // автор [страна ==" Англия "]" станд :: вектор < имя >  AUTHOR_NAMES  =  оценка ( корень ,  catalog_  >>  DescendantsOf ( catalog_ ,  author_ )  >>  Выбрать ( author_ ,  [ ] ( const  author  &  a )  {  return  a . country ()  ==  "England" ;  })  >>  name_ );

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

  • Оператор SELECT вместе с его предложениями FROM и WHERE в SQL

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

  1. ^ Тернер, Дэвид (2012). «Немного истории функциональных языков программирования» (PDF) . Международный симпозиум по тенденциям в функциональном программировании, Шпрингер, Берлин, Гейдельберг . С. 1–20.
  2. ^ Понятия, обозначение запросов для DBPL
  3. ^ Функциональные внутренности системы запросов Клейсли
  4. ^ «2.1 Шаги локации» . XML Path Language (XPath) . W3C . 16 ноября 1999 года Архивировано из оригинала 9 декабря 2012 года . Источник +24 декабрю 2 008 .
  5. ^ "Выражения XQuery FLWOR" . W3Schools . Архивировано из оригинала на 2011-10-08.
  6. ^ «Понимание списка с одной переменной в C ++ с использованием макросов препроцессора» . Архивировано из оригинала на 2011-08-21 . Проверено 9 января 2011 .
  7. ^ "Составление списков C ++" . Архивировано из оригинала на 2017-07-07 . Проверено 9 января 2011 .
  8. ^ «Язык для встроенных запросов и обхода (LEESA)» .
  • Понимание списков в бесплатном он-лайн словаре по вычислительной технике, редактор Денис Хоу.
  • Триндер, Фил (1992). «Понимания, нотация запросов для DBPL» . Труды третьего международного семинара по языкам программирования баз данных: групповые типы и постоянные данные, Нафплион, Греция . С. 55–68.
  • Уодлер, Филипп (1990). «Понимающие монады» . Материалы конференции ACM 1990 г. по LISP и функциональному программированию, Ницца .
  • Вонг, Лимсун (2000). «Функциональные внутренности системы запросов Kleisli» . Материалы пятой международной конференции ACM SIGPLAN по функциональному программированию . Международная конференция по функциональному программированию. С. 1–10.

Haskell [ править ]

  • Отчет Haskell 98, глава 3.11 Составление списков .
  • Руководство пользователя системы компиляции Glorious Glasgow Haskell, глава 7.3.4 Параллельные списки .
  • Руководство пользователя Hugs 98, глава 5.1.2 Параллельные списки понимания (также известные как zip-выражения) .

OCaml [ править ]

  • Батареи OCaml в комплекте
  • Языковые расширения, представленные в OCaml Batteries Included

Python [ править ]

  • Учебное пособие по Python, понимание списков .
  • Справочник по языку Python, отображается список .
  • Предложение по усовершенствованию Python PEP 202: составление списков .
  • Справочник по языку Python, выражения генератора .
  • Предложение по усовершенствованию Python PEP 289: Генераторные выражения .

Common Lisp [ править ]

  • Реализация макроса понимания Лиспа от Гая Лапальма

Clojure [ править ]

  • Документация Clojure API - для макросов

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

  • Примеры потоков Axiom

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

  • SQL-подобные операции над наборами с однострочными списками в Python Cookbook
  • Обсуждение представлений списков в Scheme и связанных конструкциях
  • Составьте список понятий на разных языках