Я нахожусь в процессе разработки нового языка программирования для решения некоторых бизнес-требований, и этот язык предназначен для начинающих пользователей. Так что в языке нет поддержки обработки исключений, и я не ожидаю, что они будут использовать ее, даже если я добавлю ее.
Я дошел до того, что мне нужно реализовать оператор деления, и мне интересно, как лучше всего обработать ошибку деления на ноль?
Кажется, у меня есть только три возможных способа справиться с этим делом.
- Игнорируйте ошибку и выдайте
0
результат. Регистрация предупреждения, если это возможно. - Добавьте
NaN
в качестве возможного значения для чисел, но это вызывает вопросы о том, как обрабатыватьNaN
значения в других областях языка. - Завершите выполнение программы и сообщите пользователю о серьезной ошибке.
Вариант № 1 кажется единственным разумным решением. Вариант № 3 не практичен, так как этот язык будет использоваться для запуска логики как ночной хрон.
Каковы мои альтернативы обработке ошибки деления на ноль, и каковы риски при использовании опции # 1.
programming-languages
error-handling
Reactgular
источник
источник
reject "Foo"
было реализовано, а просто отклонить документ, если он содержит ключевое словоFoo
. Я стараюсь сделать язык как можно более легким для чтения, используя термины, с которыми знаком пользователь. Предоставление пользователю их собственного языка программирования дает им возможность добавлять бизнес-правила, не завися от технического персонала.Ответы:
Я настоятельно рекомендую против # 1, потому что игнорирование ошибок является опасным анти-паттерном. Это может привести к трудным для анализа ошибкам. Установка результата деления на ноль в 0 не имеет никакого смысла, и продолжение выполнения программы с бессмысленным значением вызовет проблемы. Особенно, когда программа работает без присмотра. Когда интерпретатор программы замечает, что в программе есть ошибка (а деление на ноль почти всегда является ошибкой проектирования), ее прерывание и сохранение всего как есть обычно предпочтительнее, чем заполнение вашей базы данных мусором.
Кроме того, вы вряд ли добьетесь успеха, если будете тщательно следовать этому шаблону. Рано или поздно вы столкнетесь с ошибочными ситуациями, которые просто нельзя игнорировать (например, из-за нехватки памяти или переполнения стека), и вам все равно придется реализовать способ завершения программы.
Вариант № 2 (с использованием NaN) будет небольшой работой, но не так много, как вы думаете. Как обрабатывать NaN в различных вычислениях хорошо документировано в стандарте IEEE 754, так что вы, скорее всего, можете просто делать то, на чем говорит ваш переводчик.
Между прочим: создание языка программирования, используемого непрограммистами, - это то, что мы пытаемся сделать с 1964 года (Dartmouth BASIC). До сих пор мы были неудачными. Но все равно удачи.
источник
PHP
оказал плохое влияние на меня.NaN
язык начинающих, но в целом отличный ответ.Это не очень хорошая идея. Вообще. Люди начнут зависеть от этого, и если вы когда-нибудь это исправите, вы сломаете много кода.
Вы должны обрабатывать NaN так, как это делают среды выполнения других языков: любые дальнейшие вычисления также дают NaN, а каждое сравнение (даже NaN == NaN) дает ложь.
Я думаю, что это приемлемо, но не обязательно для новичков.
Это лучшее решение, я думаю. Имея эту информацию в руках, пользователи должны иметь возможность обрабатывать 0. Вы должны предоставить среду тестирования, особенно если она предназначена для запуска раз в ночь.
Там также четвертый вариант. Сделать разделение троичной операцией. Любой из этих двух будет работать:
источник
NaN == NaN
бытьfalse
, то вам придется добавитьisNaN()
функцию так , чтобы пользователи могли обнаружитьNaN
с.isNan(x) => x != x
. Тем не менее, когда выNaN
подходите к своему программному коду, вы не должны начинать добавлятьisNaN
проверки, а скорее отслеживать причину и делать необходимые проверки там. Поэтому важно,NaN
чтобы они размножались полностью.NaN
в основном нелогичные. На языке начинающих, они мертвы по прибытии.1/0
- с этим нужно что-то делать. Не может быть полезного результата, кромеInf
илиNaN
- что-то, что будет распространять ошибку дальше в программу. В противном случае единственным решением является остановка с ошибкой на этом этапе.Завершите работающее приложение с предубеждением. (Предоставляя адекватную отладочную информацию)
Затем обучите своих пользователей определять и обрабатывать условия, при которых делитель может быть равен нулю (введенные пользователем значения и т. Д.)
источник
В Haskell (и аналогично в Scala), вместо того , чтобы бросать исключения (или возвращать пустые ссылки) типы обертки
Maybe
иEither
могут быть использованы. УMaybe
пользователя есть возможность проверить, является ли полученное им значение «пустым», или он может предоставить значение по умолчанию при «разворачивании».Either
аналогично, но может использоваться, возвращает объект (например, строку ошибки), описывающий проблему, если таковой имеется.источник
error "some message"
оцениваемая функция.Haskell
не позволяет чистым выражениям генерировать исключения.Другие ответы уже рассмотрели относительные достоинства ваших идей. Я предлагаю другой: использовать базовый анализ потока, чтобы определить, может ли переменная быть нулевой. Тогда вы можете просто запретить деление на переменные, которые потенциально равны нулю.
Кроме того, есть интеллектуальная функция подтверждения, которая устанавливает инварианты:
Это так же хорошо, как и выдача ошибки времени выполнения - вы полностью исключаете неопределенные операции - но имеет то преимущество, что путь к коду не нужно даже искать для потенциального сбоя. Это можно сделать во многом подобно обычной проверке типов, оценивая все ветви программы с вложенными средами ввода для отслеживания и проверки инвариантов:
Кроме того, он естественным образом распространяется на диапазон и
null
проверку наличия таких возможностей на вашем языке.источник
def foo(a,b): return a / ord(sha1(b)[0])
. Статический анализатор не может инвертировать SHA-1. Clang имеет такой тип статического анализа и отлично подходит для поиска мелких ошибок, но есть множество случаев, с которыми он не может справиться.Число 1 (вставьте неустранимый ноль) всегда плохо. Выбор между # 2 (распространять NaN) и # 3 (убить процесс) зависит от контекста и в идеале должен быть глобальным, как в Numpy.
Если вы делаете одно большое интегрированное вычисление, распространение NaN - плохая идея, потому что в конечном итоге оно распространится и заразит весь ваш расчет - когда вы посмотрите на результаты утром и увидите, что все они являются NaN, вы Я должен выбросить результаты и начать все равно заново. Было бы лучше, если бы программа завершилась, вы получили звонок среди ночи и исправили его - по крайней мере, с точки зрения количества потраченных часов.
Если вы выполняете много небольших, в основном независимых вычислений (таких как сокращение карты или смущающие параллельные вычисления), и вы можете допустить, что некоторый процент из них будет непригодным для использования из-за NaN, это, вероятно, лучший вариант. Завершение программы и невыполнение 99%, которые были бы хорошими и полезными из-за 1%, которые искажены и делятся на ноль, могут быть ошибкой.
Другая опция, связанная с NaN: та же спецификация IEEE с плавающей точкой определяет Inf и -Inf, и они распространяются не так, как NaN. Например, я уверен, что Inf> любое число и -Inf <любое число, что было бы тем, что вы хотели, если бы ваше деление на ноль произошло, потому что ноль просто должен был быть небольшим числом. Если ваши входы округлены и имеют ошибку измерения (например, физические измерения, сделанные вручную), разница двух больших величин может привести к нулю. Без деления на ноль вы бы получили большое число, и, возможно, вам все равно, насколько оно велико. В этом случае In и -Inf - совершенно правильные результаты.
Это также может быть формально правильно - просто скажем, что вы работаете в расширенных реалах.
источник
Конечно, это практично: программисты обязаны писать программы, которые действительно имеют смысл. Деление на 0 не имеет никакого смысла. Поэтому, если программист выполняет деление, он также обязан заранее проверить, что делитель не равен 0. Если программист не может выполнить эту проверку достоверности, то он должен осознать эту ошибку, как только возможные и ненормализованные (NaN) или неправильные (0) результаты вычислений просто не помогут в этом отношении.
Вариант 3 оказался тем, который я бы порекомендовал вам, между прочим, за то, что он самый простой, честный и математически правильный.
источник
Мне кажется плохой идеей запускать важные задачи (например, «ночной cron») в среде, где ошибки игнорируются. Это ужасная идея, чтобы сделать это особенность. Это исключает варианты 1 и 2.
Вариант 3 является единственным приемлемым решением. Исключения не должны быть частью языка, но они являются частью реальности. Ваше сообщение о прекращении должно быть как можно более конкретным и информативным об ошибке.
источник
IEEE 754 на самом деле имеет четко определенное решение для вашей проблемы. Обработка исключений без использования
exceptions
http://en.wikipedia.org/wiki/IEEE_floating_point#Exception_handlingТаким образом, все ваши операции имеют математический смысл.
\ lim_ {x \ to 0} 1 / x = Inf
На мой взгляд, лучше всего придерживаться стандарта IEEE 754, поскольку он гарантирует, что ваши расчеты будут такими же правильными, как и на компьютере, и вы также согласуетесь с поведением других языков программирования.
Единственная проблема, которая возникает, заключается в том, что Inf и NaN будут загрязнять ваши результаты, и ваши пользователи не будут точно знать, откуда возникла проблема. Взгляните на такой язык, как Джулия, который делает это довольно хорошо.
Ошибка деления правильно распространяется через математические операции, но в конце пользователь не обязательно знает, из какой операции происходит ошибка.
edit:
Я не видел вторую часть ответа Джима Пиварски, о чем я и говорю выше. Виноват.источник
SQL, легко язык, наиболее широко используемый непрограммистами, делает № 3, чего бы это ни стоило. По моему опыту, наблюдая и помогая непрограммистам писать SQL, это поведение, как правило, хорошо понимается и легко компенсируется (с помощью оператора case или тому подобного). Помогает то, что полученное вами сообщение об ошибке имеет тенденцию быть довольно прямым, например, в Postgres 9 вы получите «ОШИБКА: деление на ноль».
источник
Я думаю, что проблема "ориентирована на начинающих пользователей. -> Так что нет поддержки ..."
Почему вы думаете, что обработка исключений проблематична для начинающих пользователей?
Что хуже? У вас есть «сложная» функция или нет понятия, почему что-то случилось? Что может смутить больше? Сбой с дампом ядра или «Неустранимая ошибка: делить на ноль»?
Вместо этого, я думаю, что FAR лучше стремиться к БОЛЬШИМ ошибкам в сообщениях. Вместо этого сделайте следующее: «Плохой расчет, разделите 0/0» (то есть: всегда показывайте ДАННЫЕ, которые вызывают проблему, а не только вид проблемы). Посмотрите, как PostgreSql делает сообщения об ошибках, это здорово, ИМХО.
Однако вы можете посмотреть на другие способы работы с исключениями, такими как:
http://dlang.org/exception-safe.html
У меня также есть мечта о создании языка, и в этом случае я думаю, что сочетание «Может быть / Необязательно» с обычными исключениями может быть лучшим:
источник
На мой взгляд, ваш язык должен предоставить общий механизм для обнаружения и обработки ошибок. Ошибки программирования должны быть обнаружены во время компиляции (или как можно раньше) и обычно приводить к завершению программы. Ошибки, возникающие из-за неожиданных или ошибочных данных или из-за непредвиденных внешних условий, должны быть обнаружены и предоставлены для соответствующих действий, но позволяют программе продолжать работу, когда это возможно.
Возможные действия включают в себя (a) завершение (b) запрос пользователя на действие (c) запись ошибки (d) замена исправленного значения (e) установка индикатора для тестирования в коде (f) вызов процедуры обработки ошибок. Какие из них вы делаете доступными и какими средствами вы должны сделать выбор.
По моему опыту, обычные ошибки данных, такие как неправильные преобразования, деление на ноль, переполнение и значение вне диапазона, являются доброкачественными и должны по умолчанию обрабатываться путем замены другого значения и установки флага ошибки. (Не программист), использующий этот язык, увидит ошибочные данные и быстро поймет, что нужно проверять ошибки и обрабатывать их.
[Для примера рассмотрим электронную таблицу Excel. Excel не прерывает вашу электронную таблицу, потому что число переполнено или что-то еще. Ячейка получает странное значение, и вы выясняете причину и исправляете ее.]
Итак, чтобы ответить на ваш вопрос: вы, конечно, не должны прекратить. Вы можете заменить NaN, но вы не должны делать это видимым, просто убедитесь, что вычисление завершено и генерирует странное высокое значение. И установите флаг ошибки, чтобы пользователи, которым это необходимо, могли определить, что произошла ошибка.
Раскрытие информации: я создал именно такую языковую реализацию (Powerflex) и решил именно эту проблему (и многие другие) в 1980-х годах. За последние 20 лет или около того не было никакого прогресса в отношении языков для непрограммистов, и вы привлечете массу критики за попытки, но я действительно надеюсь, что у вас все получится.
источник
Мне понравился троичный оператор, в котором вы предоставляете альтернативное значение в случае, если в качестве числителя установлено значение 0.
Еще одна идея, которую я не увидел, - создать общее «неверное» значение. В общем, «эта переменная не имеет значения, потому что программа сделала что-то плохое», что несет в себе полную трассировку стека. Затем, если вы когда-либо будете использовать это значение где-либо, результат снова будет недопустимым, а новая операция будет предпринята сверху (т. Е. Если недопустимое значение когда-либо появится в выражении, все выражение даст недопустимое значение, и вызовы функций не будут предприниматься; исключение будет быть булевыми операторами - истина или недействительность - истина, ложь и недействительность - ложь - могут быть и другие исключения. Как только на это значение больше нигде не ссылаются, вы записываете хорошее длинное описание всей цепочки, где что-то не так, и продолжаете работать как обычно. Может быть, отправить след по электронной почте руководителю проекта или что-то.
Что-то вроде монады «Может быть». Он будет работать и со всем, что может потерпеть неудачу, и вы можете позволить людям создавать своих собственных инвалидов. И программа будет продолжать работать до тех пор, пока ошибка не будет слишком глубокой, что, как мне кажется, действительно нужно здесь.
источник
Есть две фундаментальные причины деления на ноль.
Для 1. Вы должны сообщить пользователям, что они допустили ошибку, потому что именно они несут ответственность, и они лучше всех знают, как исправить ситуацию.
Для 2. Это не ошибка пользователя, вы можете указать на алгоритм, аппаратную реализацию и т. Д., Но это не ошибка пользователя, поэтому вы не должны завершать программу или даже генерировать исключение (если это разрешено, что не в данном случае). Таким образом, разумное решение состоит в том, чтобы продолжить операции каким-либо разумным способом.
Я вижу, что человек, задающий этот вопрос, задан для случая 1. Так что вам нужно связаться с пользователем. Используя любой стандарт с плавающей запятой, Inf, -Inf, Nan, IEEE не подходит для этой ситуации. Принципиально неверная стратегия.
источник
Запретить это на языке. То есть запрещать деление на число до тех пор, пока оно не станет доказуемо ненулевым, обычно сначала проверяя его. То есть.
источник
int
допускает нулевые значения, но GCC все еще может определить, где в коде определенные целые не могут быть нулевыми.Когда вы пишете язык программирования, вы должны воспользоваться этим фактом и сделать обязательным включение действия для устройства с нулевым состоянием. a <= n / c: 0 деление на ноль
Я знаю, что я только что предложил, по сути, добавить «goto» к вашему PL.
источник