Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Список функций. Список шаблонов




Список функций

Сейчас вы добавите уровень абстракции. Вы начали с определения списка правил: если верно это, сделай то, иначе обращайтесь к следующему правилу. Давайте временно усложним часть программы, так что вы сможете упростить другую ее часть.

import re

def match_sxz(noun):
return re. search('[sxz]$', noun)

def apply_sxz(noun):
return re. sub('$', 'es', noun)

def match_h(noun):
return re. search('[^aeioudgkprt]h$', noun)

def apply_h(noun):
return re. sub('$', 'es', noun)

def match_y(noun): ①
return re. search('[^aeiou]y$', noun)

def apply_y(noun): ②
return re. sub('y$', 'ies', noun)

def match_default(noun):
return True

def apply_default(noun):
return noun + 's'

rules = ((match_sxz, apply_sxz), ③
(match_h, apply_h),
(match_y, apply_y),
(match_default, apply_default)
)

def plural(noun):
for matches_rule, apply_rule in rules: ④
if matches_rule(noun):
return apply_rule(noun)

  1. Теперь каждое правило-условие совпадения является отдельной функцией которая возвращает результаты вызова функции re. search().
  2. Каждое правило-действие также является отдельной функцией, которая вызывает функцию re. sub() чтобы применить соответствующее правило формирования множественного числа.
  3. Вместо одной функции (plural()) с несколькими правилами у вас теперь есть структура данных rules, являющаяся последовательностью пар функций.
  4. Поскольку правила развернуты в отдельной структуре данных, новая функция plural() может быть сокращена до нескольких строк кода. Используя цикл for, из структуры rules можно извлечь правила условия и замены одновременно. При первой итерации for цикла, match_rules станет match_sxz, а apply_rule станет apply_sxz. Во время второй итерации, если мы до нее дойдем, matches_rule будет присвоено match_h, а apply_rule станет apply_h. Функция гарантированно вернет что-нибудь по окончании работы, потому что последнее правило совпадения (match_default) просто возвращает True, подразумевая, что соответствующее правило замены (apply_default) всегда будет применено.

Причиной, по которой этот пример работает, является тот факт, что в Python все является объектом, даже функции. Структура данных rules содержит функции — не имена функций, а фактические функции-объекты. Когда они присваиваются в for цикле, matches_rule и apply_rule являются настоящими функциями, которые вы можете вызывать. При первой итерации for цикла, это эквивалентно вызову matches_sxz(noun), и если она возвращает совпадение, вызову apply_sxz(noun).

-> Переменная «rules» — это последовательность пар функций. [waр-robin. сom]

Если этот дополнительный уровень абстракции сбивает вас с толку, попробуйте развернуть функцию, чтобы увидеть, что мы получаем то же самое. Весь цикл for эквивалентен следующему:

def plural(noun):
if match_sxz(noun):
return apply_sxz(noun)
if match_h(noun):
return apply_h(noun)
if match_y(noun):
return apply_y(noun)
if match_default(noun):
return apply_default(noun)

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

  1. Получить правило совпадения
  2. Правило срабатывает? Тогда применить правило замены и вернуть результат.
  3. Нет совпадений? Начать с пункта 1.

Правила могут быть определены где угодно, любым способом. Для функции plural() абсолютно нет никакой разницы.

Итак, добавление этого уровня абстракции стоило того? Вообще-то пока нет. Попробуем представить, что потребуется для добавления нового правила в функцию. В первом примере этого потребовало бы добавить новую конструкцию if в функцию plural(). Во втором примере, это потребовало бы добавить две функции, match_foo() и apply_foo(), а затем обновить последовательность rules чтобы указать, когда новые правила совпадения и замены должны быть вызваны по отношению к остальным правилам.

Но на самом деле это только средство, чтобы перейти к следующей главе. Двигаемся дальше…

Список шаблонов

Определение отдельных именованных функций для каждого условия и правила замены вовсе не является необходимостью. Вы никогда не вызываете их напрямую; вы добавляете их в последовательность rules и вызываете их через нее. Более того, каждая функция следует одному из двух шаблонов. Все функции совпадения вызывают re. search(), а все функции замены вызывают re. sub(). Давайте исключим шаблоны, чтобы объявление новых правил было более простым.

import re

def build_match_and_apply_functions(pattern, search, replace):
def matches_rule(word): ①
return re. search(pattern, word)
def apply_rule(word): ②
return re. sub(search, replace, word)
return (matches_rule, apply_rule) ③

  1. build_match_and_apply_functions() — это функция, которая динамически создает другие функции. Она принимает pattern, search и replace, затем определяет функцию matches_rule(), которая вызывает re. search() с шаблоном pattern, переданный функции build_match_and_apply_functions() в качестве аргумента, и word, который передан функции matches_rule(), которую вы определяете.
  2. Строим функцию apply тем же способом. Функция apply — это функция, которая принимает один параметр, и вызывает re. sub() с search и replace параметрами, переданными функции build_match_and_apply_functions, и word, переданным функции apply_rule(), которую вы создаете. Подход, заключающийся в использовании значений внешних параметров внутри динамической функции называется замыканиями. По сути вы определяете константы в функции замены: он принимает один параметр (word), но затем действует используя его и два других значения (search и replace), которые были установлены в момент определения функции замены.
  3. В конце концов функция build_match_and_apply_functions() возвращает кортеж с двумя значениями, двумя функциями, которые вы только что создали. Константы, которые вы определили внутри тех функций (pattern внутри функции match_rule()), search и replace в функции apply_rule()) остаются с этими функциями, даже. Это безумно круто.

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

patterns = \ ①
(
('[sxz]$', '$', 'es'),
('[^aeioudgkprt]h$', '$', 'es'),
('(qu|[^aeiou])y$', 'y$', 'ies'),
('$', '$', 's') ②
)
rules = [build_match_and_apply_functions(pattern, search, replace) ③
for (pattern, search, replace) in patterns]

  1. Наши правила формирования множественного числа теперь определены как кортеж кортежей строк (не функций). Первая строка в каждой группе — это регулярное выражение, которое вы бы использовали в re. search() чтобы определить, подходит ли данное правило. Вторая и третья строки в каждой группе — это выражения для поиска и замены, которые вы бы использовали в re. sub() чтобы применить правило и преобразовать существительное во множественное число.
  2. В альтернативном правиле есть небольшое изменение. В прошлом примере функция match_default() просто возвращает True, подразумевая, что если ни одно конкретное правило не применилось, код должен просто добавить s в конец данного слова. Функционально данный пример делает то же самое. Окончательное регулярное выражение узнает, заканчивается ли слово ($ ищет конец строки). Конечно же, у каждой строки есть конец, даже у пустой, так что выражение всегда срабатывает. Таким образом, она служит той же цели, что и функция match_default(), которая всегда возвращала True: она гарантирует, что если нет других конкретных выполненных правил, код добавляет s в конец данного слова.
  3. Это волшебная строка. Она принимает последовательность строк в patterns и превращает их в последовательность функций. Как? «Отображением» строк в функцию build_and_apply_functions(). То есть она берет каждую тройку строк и вызывает функцию build_match_and_apply_functions() с этими тремя строками в качестве аргументов. Функция build_match_and_apply_functions() возвращает кортеж из двух функций. Это означает, что rules в конце концов функционально становится эквивалентной предыдущему примеру: список кортежей, где каждый кортеж — это пара функций. Первая функция — это функция совпадения, которая вызывает re. search(), а вторая функция — применение правила (замена), которая вызывает re. sub().

Завершим эту версию скрипта главной точкой входа, функцией plural().

def plural(noun):
for matches_rule, apply_rule in rules: ①
if matches_rule(noun):
return apply_rule(noun)

  1. Поскольку список rules — тот же самый, что и в предыдущем примере (да, так и есть), нет ничего удивительного в том, что функция plural() совсем не изменилась. Она является полностью обобщенной; она принимает список функций-правил и вызывает их по порядку. Ее не волнует, как определены правила. В предыдущем примере они были определены как отдельные именованные функции. Теперь же они создаются динамически сопоставлением результата функции build_match_and_apply_functions() списку обычных строк. Это не играет никакой роли. функция plural() продолжает работать как и раньше.
Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...