17324

Элементы функционального программирования в C#

Лекция

Информатика, кибернетика и программирование

Лекция 10. Элементы функционального программирования в C План 1. Элементы функционального программирования в C 2. Делегаты 3. Лямбдавыражения и лямбдафункции 1. Элементы функционального программирования в C Даже из названия функциональное программирован...

Украинкский

2013-06-30

115.85 KB

13 чел.

Лекция 10. Элементы функционального программирования в C#

План

1. Элементы функционального программирования в C#

2. Делегаты

3. Лямбда-выражения и лямбда-функции

1. Элементы функционального программирования в C#

Даже из названия «функциональное программирование» ясно, что основной упор в нем делается на функции (в математическом смысле), т. есть, y=f(x). Функциональное вычисление – функция (или если быть точнее – «чистая функция», pure function) – должна принимать некоторые аргументы (входящие данные) на входе, производить вычисление и возвращать некоторый результат. При этом функция не должна создавать никаких побочных эффектов. Под побочными эффектами понимается возможность функции:

  1.  Изменять глобальные (статические в терминах C#) переменные.
  2.  Вызывать другие функции, которые могут создать побочный эффект.
  3.  Заниматься любым вводом/выводом внутри функции.
  4.  Посылать или принимать некие сообщения.

По сути, пункты 2-4 представляют собой разновидности одного и того же – изменение состояния посредством вызова.

Таким образом, функция не имеет право делать ничего, что могло бы изменить состояние чего бы то ни было (например, переменных в памяти). Все, что может сделать функция – это произвести вычисления и выдать результат.

Программирование в функциональном стиле позволяет сделать код короче и безопаснее.

C# не является полностью языком функционального программирования, но содержит элементы, позволяющие писать программы в функциональном стиле.

К основным элементам функционального программирования, поддерживаемых в C#, можно отнести: делегаты, лямбда-выражения и деревья выражений, а также новый интегрированный язык запросов Language Integrated Query (LINQ).

2. Делегаты

Делегаты в C# являются аналогом ссылки на функцию C++. Сама по себе функция (метод) или ее имя не является чем-то, чем можно было бы манипулировать, но если поместить ее в делегат, появится возможность ссылаться на нее, а значит передавать в другие функции и возвращать функции из других функций. Таким образом, можно (с большой натяжкой) сказать, что делегаты являются аналогами функциональных типов в языках функционального программирования.

Основное применение делегатов с C# - обработка событий.

Многие классы стандартной библиотеки .Net Framework используют делегаты для уведомления о произошедших в них событиях. Соответственно, разработчики среды .NET сочли, что будет целесообразно ввести общий стандартный тип делегата, который будет использоваться во всей библиотеке. Его прототип представлен ниже.

public delegate void EventHandler(

  // Ссылка на объект, вызвавший событие.

  object sender,    

  // Параметры, описывающие событие.  

  EventArgs e

);

Этот делегат принимает всего лишь два параметра, что явно маловато для универсального делегата. Но все же оказывается, параметров с лихвой хватает для передачи любой информации. Сам по себе класс EventArgs не содержит ни одного члена, способного передавать какую бы то ни было информацию. Он введен лишь для обобщения. Когда необходимо передать дополнительную информацию, вводится новый класс, производный от EventArgs, в котором уже и вводятся поля, передающие необходимую информацию. В одной общей библиотеке у класса EventArgs 100 потомков, и их количество от версии к версии среды исполнения неуклонно растет.

При обращении к событию, имеющему тип данного делегата, в качестве параметра необходимо передать ссылку на текущий объект (this), а во втором параметре экземпляр класса EventArgs или производного от него.

SomeEvent(this, EventArgs.Empty);

где свойство Empty попросту возвращает пустой экземпляр типа EventArgs. В принципе, его можно создать и самому воспользовавшись оператором new.

SomeEvent(this, new EventArgs());

Пример

Приложение для генерации вариантов тестов. Используются стандартые делегаты.

Одни и те же действия можно выполнить при нажатии кнопки, выбором пункта меню или кнопки панели инструментов. Например, генерация вариантов.

     private void btmGentest_Click(object sender, EventArgs e)

       {

//кнопка Button – фрагмент кода

           DateTime Date;  //дата с календаря в формате DateTime

           //Снимаем данные с полей формы

           string Disc = txtDisc.Text;                     //Дисциплина

           string Teacher = comboBox1.Text;            //Преподаватель

           try

           {

               if (Disc.Length == 0)

               {

                   throw new Exception("Введіть назву дисципліни");

               }

               if (Teacher.Length == 0)

               {

                   throw new Exception("Введіть прізвище викладача");

               }

               Date = monthCalendar1.SelectionRange.Start;   //дата, выбрання на календаре. По умолчанию, текущая дата

               string strDate = Date.ToString();             //преобразование даты в строку

               int numberVariant = (int)numericUpDown1.Value; //к-во вариантов заданий.

           

}

---------------------------  ----------------------------------

      private void toolStripButton1_Click(object sender, EventArgs e)

       {

           //кнопка панели инструментов Генерувати - делегат события btmGentest_Click

           btmGentest_Click(this, new EventArgs());

       }

--------- ------------------- ------------------------------

       private void генеруватиToolStripMenuItem1_Click(object sender, EventArgs e)

       {

           //меню Генерувати

           //кнопка генерации - делегат события btmGentest_Click

            btmGentest_Click(this, new EventArgs());

}

3. Лямбда-выражения и лямбда-функции

Функциональное программирование основано на лямбда исчислении, предложенным Алонзо Черчем.

Википедия дает такое определение:

Ля́мбда-исчисле́ние (λ-исчисление, лямбда-исчисление) — формальная система, разработанная американским математиком Алонзо Чёрчем, для формализации и анализа понятия вычислимости.

λ-исчисление может рассматриваться как семейство прототипных языков программирования. Их основная особенность состоит в том, что они являются языками высших порядков. Тем самым обеспечивается систематический подход к исследованию операторов, аргументами которых могут быть другие операторы, а значением также может быть оператор. Языки в этом семействе являются функциональными, поскольку они основаны на представлении о функции или операторе, включая функциональную аппликацию и функциональную абстракцию.

Базисом функционального программирования являются лямбда выражения и анонимные функции.

Анонимные функции позволяют создать функции, у которых нет имени.

В контексте языков программирования под лямбда-функцией понимают анонимную функцию. Т.е. функцию, которой не сопоставлено никакое имя, просто значение функционального типа.

Анонимная функция – это оператор или выражение "inline", которое можно использовать каждый раз, когда ожидается тип делегата. Ее можно использовать для инициализации именованного делегата или подставить вместо типа именованного делегата в качестве параметра метода.

Существует два типа анонимных функций:

  1.  Лямбда-выражения
  2.  Анонимные методы  

Лямбда-выражение — это анонимная функция, которая содержит выражения и операторы и может использоваться для создания делегатов или типов дерева выражений.

Во всех лямбда-выражениях используется лямбда-оператор =>, который читается как "переходит в". Левая часть лямбда-оператора определяет параметры ввода (если таковые имеются), а правая часть содержит выражение или блок оператора.

Лямбда-выражение x => x * x читается как "x переходит в x x раз". Это выражение может быть назначено типу делегата следующим образом:

delegate int del(int i);

static void Main(string[] args)

{

   del myDelegate = x => x * x;

   int j = myDelegate(5); //j = 25

}

Оператор => имеет тот же приоритет, что и оператор присваивания (=) и является правоассоциативным.

Многие языки программирования поддерживают работу с анонимными функциями.

Примеры

С#

delegate(int x){return x +1; }

Pyton

lambda x: x + 1

Lisp

(lambda (x) (+ x 1))

PHP (http://blog.kron0s.com/anonymous-functions-in-php )

Переменные-функции

До того как сказать что-либо об анонимных функциях, взглянем на концепцию PHP известную как переменные-функции. Она заключается в том, что если добавить к переменной круглые скобки, то интерпретатор PHP сначала проверит, не существует ли функции с именем равным значению переменной и если она есть - выполнить ее. Это можно увидеть на следующем примере:

  1.  function Hello($name)
  2.  {
  3.      echo "Привет, $name";
  4.  }

Мы можем вызвать эту функцию, используя переменную, хранящую ее имя. И это может быть очень полезно, если имя функции не определенно до момента выполнения.

  1.  $func = "Hello";
  2.  $func("Мир!");
  3.   
  4.  // результат:
  5.  //Привет, Мир!

Другой пример, используя класс и статический метод:

  1.  <?php
  2.  class CHello
  3.  {
  4.      static function Hello($name)
  5.      {
  6.          echo "Привет, $name";
  7.      }
  8.  }
  9.  $func = "Hello";
  10.  CHello::$func("Мир!");
  11.   
  12.  // результат:
  13.  // Привет, мир!
  14.  ?>

Анонимные или лямбда функцииc php

Бывает, необходимо создать небольшую локальную функцию, состоящую всего из нескольких строк, например, для обратного вызова. Глупо загрязнять глобальное пространство имен такого рода одноразовыми функциями. Гораздо лучше создать анонимную или лямбда функцию используя create_function.

Рассмотрим на примере:

  1.  <?php
  2.  $str = "Привет, Мир!";
  3.  $lambda = create_function('$match', 'return "Друг!";');
  4.  $str = preg_replace_callback('/Мир/', $lambda, $str);
  5.  echo $str ;
  6.  ?>

Здесь мы создали маленькую безымянную (анонимную) функцию, которая вызывается в функции preg_replace_callback. Хотя create_function позволяет создать анонимные функции, это не часть языка, а "хак". PHP 5.3 поддерживает по настоящему анонимные функции как часть языка. Мы создаем безымянную функцию и присваиваем её переменной и затем просто используем эту переменную как функцию.

Пример:

  1.  <?php
  2.  $func = function($name)
  3.  {
  4.      echo "Привет, $name\n";
  5.  };
  6.   
  7.  $func("Мир!");
  8.  $func("Алексей!");
  9.   
  10.  // результат:
  11.  //Привет, Мир!
  12.  //Привет, Алексей!
  13.  ?>

Заметьте, что в конце определения функции есть точка с запятой. Она необходима, так как такое определение функции это утверждение, а после определения утверждения всегда должна быть точка с запятой. Еще один пример:

  1.  <?php
  2.  $str = "Привет, Мир!";
  3.  $func = function($match)
  4.  {
  5.      return "друг!";
  6.  };
  7.   
  8.  $str = preg_replace_callback('/Мир/', $func, $str);
  9.  echo $str ;
  10.  ?>

Анонимные и вложенные функции

PHP позволяет вкладывать функции внутрь друг друга. Рассмотрим на примере. Функция censorString принимает строку как параметр и заменяет нужные слова в строке на "*". Функция censorString определяет вложенную функцию replace, которая используется при вызове preg_replace_callback. Учитывая то, что функция replace используется в нашей программе только в функции censorString, лучше всего определить ее как вложенную, чтобы не загрязнять пространство имен.

  1.  <?php
  2.  function censorString($string, $censor)
  3.  {
  4.      function replace($match)
  5.      {
  6.          return str_repeat("*", strlen($match[0]));
  7.      }
  8.   
  9.      return preg_replace_callback('/'.$censor.'/', 'replace', $string);
  10.   
  11.  }
  12.   
  13.  echo censorString("Привет, мир!", "мир");
  14.  echo censorString("Привет, мир!", "Привет");
  15.   
  16.  // результат:
  17.  // Привет, ***!
  18.  // Fatal error: Cannot redeclare replace()
  19.  ?>
  20.  

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

  1.  <?php
  2.   function censorString($string, $censor)
  3.  {
  4.      $func = function($match)
  5.      {
  6.          return str_repeat("*", strlen($match[0]));
  7.      };
  8.   
  9.      return preg_replace_callback('/'.$censor.'/', $func, $string);
  10.   
  11.  }
  12.   
  13.  echo censorString("Привет, мир!", "мир");
  14.  echo censorString("Привет мир!", "Привет");
  15.  
  16.  // результат:
  17.  // Привет, ***!
  18.  // ******, мир!
  19.  ?>

Теперь вложенная функция появляется только на время выполнения censorString. Итак, мы можем повторно вызывать функцию censorString без того, чтобы вызвать ошибку передекларации.

Другой способ определить вложенную функцию:

  1.  <?php
  2.  function censorString($string, $censor)
  3.  {
  4.   
  5.      return preg_replace_callback('/'.$censor.'/', 
  6.                                  function($match) 
  7.                                  {
  8.                                      return str_repeat("*", 
  9.                                             strlen($match[0]));
  10.                                  },
  11.                                  $string);
  12.   
  13.  }
  14.   
  15.  echo censorString("Привет, мир!", "мир");
  16.  echo censorString("Привет, мир!", "Привет");
  17.   
  18.  // результат:
  19.  // Привет, ***!
  20.  // ******, мир!
  21.  ?>

Замыкания

Замыкание это анонимная функция со своим контекстом. Короче говоря, это функция, которая знает о переменных, не определенных в ней. Рассмотрим на простом примере. Скажем, мы хотим создать анонимную функцию, которая умножает число на 5.

  1.  <?php
  2.  $mult = function($x)
  3.  {
  4.      return $x * 5;
  5.  };
  6.   
  7.  echo $mult(2);
  8.   
  9.  // результат:
  10.  // 10
  11.  ?>

Если мы хотим чтобы число умножалось на 7 вместо 5, нам необходимо создавать другую функцию. Вместо того чтобы создавать несколько почти одинаковых функций, создадим замыкание, используя конструкцию use, которая дает анонимной функции доступ к внешним переменным и "замыкает" их в текущей функции.

  1.  <?php
  2.  $multiply = function($multiplier)
  3.  {
  4.      return function($x) use ($multiplier)
  5.      {
  6.          return $x * $multiplier;
  7.      };
  8.  };
  9.   
  10.  // $mul5 теперь содержит функцию, которая умножает аргумент на пять
  11.  $mult5 = $multiply(5);
  12.   
  13.  // $mul7 теперь содержит функцию, которая умножает аргумент на семь
  14.  $mult7 = $multiply(7);
  15.   
  16.  echo $mult5(5);
  17.  echo $mult7(5);
  18.   
  19.  // результат:
  20.  // 25
  21.  // 35
  22.  ?>

Лямбда функции и замыкания приближают PHP по функциональности к другим современным языкам.


 

А также другие работы, которые могут Вас заинтересовать

44132. Правовое регулирование дарения в российском гражданском праве 481 KB
  Новое время изменившиеся экономико-правовое и социально-политическое направления развития государства требуют новых исследований связанных с развитием института дарения. Повышает актуальность исследования и распространенная практика совершения мнимых и притворных сделок под видом дарения с целью уклонения от уплаты налогов нарушения преимущественного права покупки участника общей долевой собственности и пр. Объектом исследования моей дипломной работы являются общественные действия и процессы связанные с договором дарения.
44133. Изучение важности и порядка проведения допроса свидетеля и потерпевшего 110.5 KB
  Допрос свидетеля потерпевшего – это следственное действие в результате которого формируется доказательство именуемое показания свидетеля. И хотя они обязательно должны фиксироваться в протоколе следственного действия протокол допроса – доказательство также как и показания свидетеля. 12 УПК оговариваются порядок вызова свидетеля потерпевшего на допрос место проведения допроса порядок проведения допроса а так же порядок ведения протокола допроса.
44134. ОСНОВНЫЕ ПРИНЦИПЫ ПРОВЕДЕНИЯ АНАЛИЗА ФИНАНСОВО-ХОЗЯЙСТВЕННОЙ ДЕЯТЕЛЬНОСТИ ОРГАНИЗАЦИИ 155.45 KB
  Бухгалтерский баланс как источник информации финансового состояния предприятия Анализ финансовых результатов организаций эффективность использования активов и источников их формирования...
44135. Механизм защиты имущественных прав ребенка в семейном праве 262 KB
  Основные имущественные права несовершеннолетних. Порядок реализации прав на получение имущественного содержания алиментные обязательства и пособия. Право собственности несовершеннолетнего на имущество и полученные им доходы от трудовой деятельности.
44136. Система автоматизированного управления ООО «ВИЗ-Сталь» 9.61 MB
  В пятом разделе проводится экономический расчет затрат на разработку и анализ целесообразности внедрения системы. Планируется дальнейшее развитие системы и разработка дополнительных модулей. Цели и назначение создания системы. Назначение системы.
44137. Стиль. Візаж 946.5 KB
  Фата рожевого з білим кольору розміром по плечі на резинці для волосся кріпиться на так звану буклю що акуратно зібрана на потилиці але чілка не зачісується назад згідно з корекцією овалу обличчя моделі. Підбирається зачіска під овал обличчя. Для виконання макіяжу знадобляться такі інструменти: пензлі для макіяжу зокрема великий пензлик для накладання пудри на обличчя пензлик для рум'ян круглий пензлик для розтушовки аплікатор звичайний пензлик середнього розміру для тіней та веєроподібний; чистий спонж; пенюар; та і нструмент для...
44138. Управління підвищенням конкурентоспроможності продукції в корпорації ДП ВО «Київприлад» 946.5 KB
  Аналітична оцінка діяльності ДП ВО Київприлад Загальна організаційноекономічна та фінансова характеристика ДП ВО Київприлад Аналіз середовища функціонування підприємства ДП ВО Київприлад Оцінка конкурентоспроможності продукції ДП ВО Київприлада
44139. РЕФЛЕКСИВНАЯ ДЕЯТЕЛЬНОСТЬ ПЕРЕВОДЧИКА КАК ФАКТОР СОЗДАНИЯ УСПЕШНОГО ПЕРЕВОДА 436.5 KB
  Предпосылки формирования понятия перевода в деятельностном аспекте. Понятие рефлексии в аспекте деятельностной теории перевода. Основные положения деятельностной теории перевода.
44140. Организационно-экономическая часть дипломных проектов, направленных на разработку программного обеспечения 297.5 KB
  Организационно-экономической части дипломного проекта для проектов связанных с разработкой программного обеспечения: Учебное пособие. Показано каким образом следует: выделить основные этапы проекта разработки нового изделия программы провести расчет трудоемкости проекта определить численность и квалификацию исполнителей построить сетевую модель и календарный график выполнения проекта провести анализ структуры затрат проекта. Уделяется внимание методам исследования рынка для разрабатываемого ПО а также планированию цены и...