Рубрики
Без рубрики

Преобразователь случая персонажа и кодов ASCII

Парадигма программирования ограничений эффективно применяется для решения группы проблем, которые могут … помеченные CS, CSP, программирование, Python.

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

Давайте возьмем, например, теорему Пифагора: a² + Анкет ограничение представлено этим уравнением, которое имеет три переменные (A, B и C), и каждый имеет домен (неотрицательный). Используя императивный стиль программирования, для вычисления любой из этих переменных, имеющих другие две, нам нужно будет создать три разные функции (потому что каждая переменная вычисляется другим уравнением):

  • c = √ (a² + b²)
  • a = √ (c² – b²)
  • b = √ (c² – a²)

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

def pythagoras(*, a=None, b=None, c=None):
    ''' Computes a side of a right triangle '''

    # Validate
    if len([i for i in (a, b, c) if i is None or i <= 0]) != 1:
        raise SystemExit("ERROR: you need to define any of two non-negative variables")

    # Compute
    if a is None:
        return (c**2 - b**2)**0.5
    elif b is None:
        return (c**2 - a**2)**0.5
    else:
        return (a**2 + b**2)**0.5

Чтобы увидеть разницу подхода программирования ограничения, я покажу пример «проблемы» с четырьмя переменными и ограничением, которое не представлено прямым математическим уравнением. Это преобразователь, который может изменить случай символов (малый в/из капитала) и вернуть коды ASCII обоих. Следовательно, в любое время преобразователь знает обо всех четырех значениях и немедленно реагирует на любые изменения. Идея создания этого примера была полностью вдохновлена Fahrenheit-Celsius Converter Джон Денеро.

Давайте посмотрим на диаграмму системы ограничений:

Представленная «проблема» переводится в систему ограничений, которая состоит из узлов (ограничения) и разъемов (переменных). Разъемы предоставляют интерфейс для получения и настройки значений. Они также проверяют домены переменных. Когда какое -то значение меняется, этот конкретный разъем уведомляет все его подключенные узлы об изменении. Узлы, в свою очередь, удовлетворяют ограничениям, рассчитывают новые значения и распространяют их на другие разъемы по всей системе, «прося» их установить новое значение. Распространение осуществляется с использованием техники передачи сообщений, которая означает, что разъемы и узлы получают сообщения (синхронно) и соответственно реагируют. Например, если система получает букву «А» на разъеме «Столица», все остальные три разъема дают соответствующий результат соответственно определенным ограничениям на узлах: 97, ‘a’ и 65. И, конечно же, не разрешается устанавливать на этот разъем каких -либо небольших букв, например, «B», потому что каждый разъем имеет свой собственный домен.

Когда все разъемы связаны с узлами, узлы определяются ограничениями, система полностью установлена и готова к получению значений на любом из четырех разъемов. После того, как он будет установлен, система автоматически вычисляет и устанавливает значения на разъемах REST. Нет необходимости проверять, какая переменная была установлена, а какие функции должны быть вызваны, так как это было необходимо в императивном подходе, который относительно легко достичь с помощью нескольких переменных, но становится интересным в случае десятков или более.

Полный исходный код доступен в моем GitHub Repo Анкет Давайте теперь немного раз углубимся в детали и выясним, как строится система. Во -первых, мы определяем наши разъемы, предоставляя им имена и устанавливая домены как функцию одного аргумента:

import constraint_programming as cp

small_ascii = cp.connector('Small Ascii', lambda x: x >= 97 and x <= 122)
small_letter = cp.connector('Small Letter', lambda x: x >= 'a' and x <= 'z')
capital_ascii = cp.connector('Capital Ascii', lambda x: x >= 65 and x <= 90)
capital_letter = cp.connector('Capital Letter', lambda x: x >= 'A' and x <= 'Z')

Во -вторых, мы связываем эти разъемы с узлами. Есть два типа: код (переводит буквы в коды ASCII и обратно) и aa (переводит небольшие буквы в капитал и обратно):

code(small_letter, small_ascii)
code(capital_letter, capital_ascii)
aA(small_letter, capital_letter)

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

def code(conn1, conn2):
    return cp.constraint(conn1, conn2, ord, chr)

def aA(conn1, conn2):
    return cp.constraint(conn1, conn2, str.upper, str.lower)

Каждый узел имеет только два разъема. Если обновление произошло на первом разъеме, то для вычисления значения другого разъема (переменная) вызвана первая функция. То же самое происходит, если второй разъем меняет свое значение. Например, если код Узел получает «А» на conn1 разъем, затем функция Ord будет использоваться для получения кода ASCII. И наоборот, если aa Узел получает «А» на conn2 соединитель, тогда он должен использовать Str.Lower Функция, чтобы получить подходящую маленькую букву на Conn1 . Каждый узел отвечает за вычисление новых значений и «отправку» сообщения другому соединителю, что есть новое значение для установки. Это сообщение передается с именем узла, который просит установить новое значение, и фактически новое значение.

def set_value(src_constr, value):
    if (not domain is None) and (not domain(value)):
        raise ValueOutOfDomain(link, value)
    link['value'] = value
    for constraint in constraints:
        if constraint is not src_constr:
            constraint['update'](link)

Когда разъем получает сообщение «Установить», он запускает функцию «set_value», чтобы проверить домен, устанавливает новое значение и отправляет сообщение «Обновление» в другой узел. Это просто уведомление о том, что значение этого разъема изменилось.

def update(src_conn):
    if src_conn is conn1:
        conn2['set'](node, constr1(conn1['value']))
    else:
        conn1['set'](node, constr2(conn2['value']))

Затем уведомленный узел запрашивает это новое значение в разъеме, вычислите новое значение для другого разъема и так далее, пока вся система не изменится. Вот как на самом деле работает распространение.

Но как происходит передача сообщения? Это реализовано как доступ к клавишам словарей. Обе функции (соединитель и ограничение) возвращают Словарь отправки Анкет Такой словарь содержит Сообщения как ключи и закрытие как значения. Допустив, что, скажем, «Set», словарь возвращает функцию «set_value» (закрытие), которая имеет доступ ко всем локальным именам функции «соединитель».

# A dispatch dictionary
link = { 'name': name,
         'value': None,
         'connect': connect,
         'set': set_value,
         'constraints': get_constraints }

return link

Наличие словаря в качестве возвращаемого значения позволяет создавать несколько закрытий (функций) с доступом к одному и тому же локальному состоянию для работы. Затем эти закрытия можно применить с помощью ключей в качестве своего рода сообщений.

Оригинал: “https://dev.to/vorakl/a-converter-of-a-character-s-case-and-ascii-codes-46p9”