Jump to content

Полупредикатная задача

В компьютерном программировании возникает проблема полупредикатов , когда подпрограмма, предназначенная для возврата полезного значения, может дать сбой, но для сигнализации об ошибке используется допустимое в противном случае возвращаемое значение . [1] Проблема в том, что вызывающий подпрограмму не может сказать, что в данном случае означает результат.

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

Практические последствия

[ редактировать ]

Ранние программисты обрабатывали потенциально исключительные случаи, такие как деление, используя соглашение, требующее, чтобы вызывающая процедура проверяла входные данные перед вызовом функции деления. Это имело две проблемы: во-первых, это сильно затрудняло весь код, выполняющий деление (очень распространенная операция); во-вторых, это нарушило принципы «Не повторяйся» и принципы инкапсуляции , первый из которых предполагает устранение дублированного кода, а второй — хранить код, связанный с данными, в одном месте (в этом примере разделения проверка входных данных выполнялась отдельно). ). Для вычислений, более сложных, чем деление, вызывающей стороне может быть сложно распознать недопустимый ввод; в некоторых случаях определение достоверности входных данных может оказаться столь же дорогостоящим, как и выполнение всех вычислений. Целевую функцию также можно изменить, и тогда она будет ожидать других предварительных условий, чем вызывающая; такая модификация потребовала бы изменений в каждом месте, где вызывалась функция.

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

Использование специального соглашения для интерпретации возвращаемых значений

[ редактировать ]

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

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

  • Программист должен запомнить конкретные значения ошибок для многих функций, которые, конечно, не могут быть идентичными, если функции имеют разные диапазоны.
  • Другая реализация одной и той же функции может использовать другое значение ошибки, что приводит к возможным ошибкам при переходе программистов из одной среды в другую.
  • Если сбойная функция желает передать полезную информацию о том, почему она не удалась, одного значения сбоя недостаточно.
  • Целое число со знаком уменьшает вдвое возможный диапазон индексов, чтобы иметь возможность хранить бит знака .
  • Хотя выбранное значение является недопустимым результатом для этой операции, оно может быть допустимым входным значением для последующих операций. Например, в Python str.find возвращает −1, если подстрока не найдена, [2] но -1 является допустимым индексом (отрицательные индексы обычно начинаются с конца [3] ).

Многозначный доход

[ редактировать ]

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

Различные методы возврата нескольких значений включают в себя:

  • Возврат кортежа значений. Это обычное явление в языках (таких как Python ), которые имеют встроенный тип данных кортеж и специальный синтаксис для их обработки: в Python x, y = f() вызывает функцию f возвращает пару значений и присваивает элементы пары двум переменным.
  • Вторичные возвращаемые значения, как в Common Lisp . Все выражения имеют основное значение, но заинтересованным вызывающим объектам могут быть возвращены вторичные значения. Например, GETHASH Функция возвращает значение данного ключа в ассоциативной карте или в противном случае значение по умолчанию. Однако он также возвращает вторичное логическое значение, указывающее, было ли найдено значение, что позволяет различать случаи «значение не найдено» и «найденное значение равно значению по умолчанию». Это отличается от возврата кортежа тем, что вторичные возвращаемые значения являются необязательными — если вызывающая сторона не заботится о них, она может полностью их игнорировать, тогда как возвращаемые значения кортежа являются просто синтаксическим сахаром для возврата и распаковки списка, и каждый вызывающий объект всегда должен знать и использовать все возвращаемые предметы.
  • Языки с вызовом по ссылке или эквивалентами, такими как вызов по адресу с использованием указателей , могут допускать многозначный возврат, назначая некоторые параметры в качестве выходных параметров . В этом случае функция может просто вернуть значение ошибки, а в функцию будет передана переменная, предназначенная для хранения фактического результата. Это аналогично использованию статуса выхода для хранения кода ошибки и потоков для возврата контента.
  • Вариант выходных параметров используется в объектно-ориентированных языках , в которых используется вызов путем совместного использования , где изменяемый объект передается функции, а объект модифицируется для возврата значений.
  • Языки логического программирования, такие как Пролог, не имеют возвращаемых значений. Вместо этого в качестве выходных параметров используются несвязанные логические переменные, которые необходимо унифицировать со значениями, созданными при вызове предиката.

Глобальная переменная для статуса возврата

[ редактировать ]

Подобно аргументу «out», глобальная переменная может хранить информацию о том, какая ошибка произошла (или просто произошла ли ошибка).

Например, если возникает ошибка, о которой сигнализируется (как правило, как указано выше, недопустимым значением, например -1), Unix errno переменная установлена, чтобы указать, какое значение произошло. Использование глобальной переменной имеет свои обычные недостатки: безопасность потоков становится проблемой (современные операционные системы используют поточно-безопасную версию errno), и если используется только одна глобальная ошибка, ее тип должен быть достаточно широким, чтобы содержать всю интересную информацию обо всех возможных ошибки в системе.

Исключения

[ редактировать ]

Исключения — одна из широко используемых схем решения этой проблемы. Состояние ошибки вообще не считается возвращаемым значением функции; нормальный поток управления нарушается, и явная обработка ошибки происходит автоматически. Они являются примером внеполосной сигнализации .

Расширение типа возвращаемого значения

[ редактировать ]

Гибридные типы, созданные вручную

[ редактировать ]

В C распространенным подходом, когда это возможно, является намеренное использование более широкого типа данных, чем это необходимо для функции. Например, стандартная функция getchar() определяется типом возвращаемого значения int и возвращает значение в диапазоне [0, 255] (диапазон unsigned char) об успехе или ценности EOF ( определяется реализацией , но вне диапазона unsigned char) в конце ввода или ошибке чтения.

Ссылочные типы, допускающие значение NULL

[ редактировать ]

В языках с указателями или ссылками одним из решений является возврат указателя на значение, а не самого значения. Затем этот указатель возврата может быть установлен в значение null, чтобы указать на ошибку. Обычно он подходит для функций, которые все равно возвращают указатель. Это имеет преимущество в производительности по сравнению со стилем обработки исключений ООП. [4] с тем недостатком, что нерадивые программисты могут не проверить возвращаемое значение, что приведет к сбою при использовании недопустимого указателя. Является ли указатель нулевым или нет, это еще один пример проблемы предикатов; null может быть флагом, указывающим на ошибку, или значением указателя, возвращенным успешно. Распространенным шаблоном в среде UNIX является установка отдельной переменной для указания причины ошибки. Примером этого является стандартная библиотека C. fopen() функция.

Неявно гибридные типы

[ редактировать ]

В динамически типизированных языках, таких как PHP и Lisp , обычным подходом является возврат false, none, или null когда вызов функции завершается неудачно. Это работает, возвращая тип, отличный от обычного возвращаемого типа (таким образом расширяя тип). Это динамически типизированный эквивалент возврата нулевого указателя.

Например, числовая функция обычно возвращает число (int или float), и хотя ноль может быть допустимым ответом, false — нет. Аналогично, функция, которая обычно возвращает строку, может иногда возвращать пустую строку в качестве допустимого ответа, но возвращать false в случае ошибки. Этот процесс жонглирования типами требует осторожности при проверке возвращаемого значения: например, в PHP используйте === (т. е. равные и одного типа), а не просто == (т.е. равны после автоматического преобразования типа). Он работает только тогда, когда исходная функция не предназначена для возврата логического значения и по-прежнему требует, чтобы информация об ошибке передавалась другими способами.

Явно гибридные типы

[ редактировать ]

В Haskell и других языках функционального программирования обычно используется тип данных, размер которого настолько велик, насколько это необходимо для выражения любого возможного результата. Например, можно написать функцию деления, возвращающую тип Maybe Realи getchar функция, возвращающая Either String Char. Первый — это тип опции , который имеет только одно значение ошибки, Nothing. Второй случай — тегированное объединение : результатом является либо некоторая строка с описательным сообщением об ошибке, либо успешно прочитанный символ. Haskell Система вывода типов помогает гарантировать, что вызывающие программы справятся с возможными ошибками. Поскольку условия ошибки становятся явными в типе функции, просмотр ее сигнатуры сразу же подсказывает программисту, как обрабатывать ошибки. Кроме того, тегированные объединения и типы опций образуют монады , если они наделены соответствующими функциями: это можно использовать для поддержания чистоты кода за счет автоматического распространения необработанных ошибок.

Rust имеет алгебраические типы данных и встроенную функцию Result<T, E> и Option<T> типы.

fn find(key: String) -> Option<String> {
    if key == "hello" {
        Some(key)
    } else {
        None
    }
}

C ++. Представлен язык программирования std::optional<T> в обновлении C++17 .

std::optional<int> find_int_in_str(std::string_view str) {
    constexpr auto digits = "0123456789";
    auto n = str.find_first_of(digits);
    if (n == std::string::npos) {
        // The string simply contains no numbers, not necessarily an error
        return std::nullopt;
    }

    int result;
    // More search logic that sets 'result'
    return result;
}

и std::expected<T, E> в C++23 обновлении

enum class parse_error {
  kEmptyString,
  kOutOfRange,
  kNotANumber
};

std::expected<int, parse_error> parse_number(std::string_view str) {
    if (str.empty()) {
        // Flag one unexpected situation out of several
        return std::unexpected(parse_error::kEmptyString);
    }

    int result;
    // More conversion logic that sets 'result'
    return result;
}

См. также

[ редактировать ]
  1. ^ Норвиг, Питер (1992). «Общее решение проблем». Парадигмы программирования искусственного интеллекта: примеры использования обычного LISP . Морган Кауфманн . п. 127. ИСБН  1-55860-191-0 .
  2. ^ «Встроенные типы» . Документация Python 3.10.4 .
  3. ^ "Если i или j отрицательно, индекс относится к концу последовательности s: len(s) + i или len(s) + j заменяется.» Общие операции с последовательностями, примечание (3) .
  4. ^ Почему исключения должны быть исключительными — пример сравнения производительности .
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: a9de6e7f6ff9c3085b6902279bdba6ee__1709165640
URL1:https://arc.ask3.ru/arc/aa/a9/ee/a9de6e7f6ff9c3085b6902279bdba6ee.html
Заголовок, (Title) документа по адресу, URL1:
Semipredicate problem - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)