17324

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

Лекция

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

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

Украинкский

2013-06-30

115.85 KB

15 чел.

Лекция 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 по функциональности к другим современным языкам.


 

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

82497. Теория потребительского поведения. Кардинализм и ординализм 37.73 KB
  максимизировать совокупную полезность. Потребительский выбор это выбор максимизирующий совокупную полезность в условиях ограниченности ресурсов дохода. Рациональное потребительское потребление предполагает что потребитель максимизирует совокупную полезность в условиях существующего у него бюджетного ограничения. Потребитель находится в состоянии равновесия если он не может увеличить совокупную полезность при данной величине своего дохода и существующих ценах увеличивая или уменьшая покупку одного или другого товара.
82498. Механизм рынка совершенной конкуренции. Равновесие фирмы в краткосрочном периоде 38.41 KB
  Совершенная конкуренция когда на рынке: мнво покупателей и продавцов высокая мобильность факторов производства отсутствие барьеров на пути вступления на рынок или выхода из него доходность продаваемой продукции равный доступ всех участников рыночных отношений к инфи Совершенно конкурентные фирмы не имеют контроля над ценами т. объем их производства по сравнению с размерами рынка ничтожно мал будет ли она производить продукции больше или меньше цены не изменятся. В одной и той же отрасли действуют разные фирмы с разным...
82499. Сравнительная характеристика французской и германской правовых групп 28.69 KB
  Внутри романогерманской правовой семьи группа римского романского права которая наиболее сильно отражена во французском праве отличается от группы германского права. Франция прошла длительную правовую историю и в основе ее современной системы источников права до сих пор лежат кодексы наполеоновской эпохи о которых подробно говорилось выше. Во французской правовой системе в качестве самостоятельного источника права признаются и общие принципы права. Во французской юридической литературе источники права делятся на две основные группы:...
82500. Характеристика правовой системы социалистических стран 25.95 KB
  Социалистическая система действительно представляет собой упрощенный и сильно идеологизированный вариант романогерманской правовой системы с более низким уровнем юридической техники. Среди специфических черт социалистической правовой системы резкое уменьшение частноправовой сферы связанное с идеями обобществления производства и уничтожения частной собственности на средства производства. В первый период развития социалистической правовой системы существенное влияние имел тезис о классовом характере социалистического права.
82501. Общая характеристика англо-саксонской правовой системы (правовой семьи общего права) 25.43 KB
  Данная семья характеризуется следующими признаками: основным источником права выступает судебный прецедент правила поведения сформулированные судьями в их решениях по конкретному делу и распространяющиеся на аналогичные дела; ведущая роль в формировании права правотворчестве отводится суду который в этой связи занимает особое положение в системе государственных органов; на первом месте находятся не обязанности а права человека и гражданина защищаемые прежде всего в судебном порядке; главенствующее значение имеет в первую очередь...
82503. Характеристика прецедента как источника права в системе общего права Англии 25.62 KB
  Решения высшей инстанции палаты лордов обязательны для всех других судов. Апелляционный суд состоящий из двух отделений гражданского и уголовного обязан соблюдать прецеденты палаты лордов и свои собственные а его решения обязательны для всех нижестоящих судов. Высокий суд все его отделения связан прецедентами обеих вышестоящих инстанций его решения обязательны для всех нижестоящих инстанций а также не будучи строго обязательны влияют на рассмотрение дел в его отделениях. Окружные и магистратские суды обязаны следовать...
82504. Общая характеристика судебной системы Великобритании 25.93 KB
  Судебная система Великобритании включает в себя: магистратские суды рассматривают как правило без участия присяжных заседателей преступления не представляющие значительной общественной опасности преступления по обвинительному акту тяжкие преступления гражданские иски административные дела; суды графств являются основными судами первой инстанции по гражданским делам; Верховный суд Великобритании. Особенности судебной системы Великобритании проявляются в наличии следующих судов: Королевский суд Лондона это высшая инстанция...
82505. Формирование американского права 27.12 KB
  Ллевелина Традиции общего права выделяются три периода развития американского права: первый с 1800 г. Богдановская выделяет четыре этапа в развитии американского права. Первый этап утверждения прецедентного права в колониальный период.