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


 

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

7852. Стадии и законы движения (развития) коллектива народного художественного творчества 26.45 KB
  Стадии и законы движения (развития) коллектива народного художественного творчества Любой коллектив в своем развитии проходит определенные стадии, которые характеризуют его качественные стороны. А.Г. Ковалев выделяет три стадии развития коллектива: ...
7853. Характеристика учреждений социально-культурной сферы 31.98 KB
  Характеристика учреждений социально-культурной сферы В конце 80-х - начале 90-х годов XX столетия в России функционально и юридически оформилось новое социокультурное направление под названием социально-культурная деятельность. В соответствии со стр...
7854. Деятельность федеральных и региональных органов управления культурой по развитию НХТ в современных условиях 18.29 KB
  Деятельность федеральных и региональных органов управления культурой по развитию НХТ в современных условиях До 1953 года сфера культуры в нашей стране координировалась Главным управлением по политико-просветительной работе (Главполитпросвет) при Нар...
7855. Технология проведения фестивалей народного художественного творчества 22.54 KB
  Технология проведения фестивалей народного художественного творчества В современных условиях роль конкурсных и фестивальных мероприятий приобретает очень важное значение для коллективов народного художественного творчества. В первую очередь важность...
7856. Сутність, принципи і роль страхування 81 KB
  Тема 1. Сутність, принципи і роль страхування План. Необхідність страхового захисту від ризикових обставин. Історія виникнення і розвитку страхування. Місце страхування в системі економічних категорій та його функції. Принципи страхування...
7857. Класифікація страхування 53.5 KB
  Тема 2. Класифікація страхування План. Призначення та основні критерії класифікації у страхуванні. Класифікація за формами проведення. Рекомендована література: Міні - лексикон: Класифікація ...
7858. Страхові ризики та їх оцінка 106 KB
  Тема 3. Страхові ризики та їх оцінка План. Поняття ризику та його основні характеристики. Види ризиків та їх оцінка. Управління ризиком. Тарифна ставка: структура та методи розрахунку. Рекомендована література Основна...
7859. Страховий ринок 130.5 KB
  Тема 4. Страховий ринок План 1. Страховий ринок і його структура. 2. Роль посередників на страховому ринку. 3. Стан та перспективи розвитку страхового ринку України. 4. Страховий маркетинг. 5. Договори страхування. Рекомендована література Основна: ...
7860. Страхова організація 70 KB
  Організаційні форми страховиків. Структура страхових компаній. Товариства взаємного страхування. Обєднання страховиків та їх функції