Подтвердить что ты не робот

Есть -1 магическое число? Анти-шаблон? Запах кода? Цитаты и рекомендации от властей

Возможный дубликат:
Постоянное злоупотребление?

Я видел -1, используемый в различных API, чаще всего при поиске в "коллекции" с нулевыми индексами, обычно для указания индекса "не найден". Это "работает", потому что -1 никогда не является юридическим индексом для начала. Кажется, что любое отрицательное число должно работать, но я думаю, что -1 почти всегда используется, как какое-то (неписаное?) Соглашение.

Я хотел бы ограничить область применения Java хотя бы на данный момент. Мои вопросы:

  • Каковы официальные слова Sun относительно использования -1 как "специального" возвращаемого значения, подобного этому?
  • Какие цитаты существуют по этой проблеме, например. Джеймс Гослинг, Джош Блох или даже другие авторитетные фигуры за пределами Java?
  • Каковы были некоторые из замечательных дискуссий по этому вопросу в прошлом?
4b9b3361

Ответ 1

Это обычная идиома на языках, где типы не включают проверки диапазона. Значение "вне границ" используется для обозначения одного из нескольких условий. Здесь возвращаемое значение указывает на две вещи: 1) был найден персонаж и 2) где он был найден. Использование -1 для not found и неотрицательный индекс для found лаконично кодирует оба из них в одно значение, а тот факт, что not-found не нужно возвращать индекс.

На языке со строгой проверкой диапазона, такой как Ada или Pascal, метод может быть реализован как (псевдокод)

   bool indexOf(c:char, position:out Positive);

Positive является подтипом int, но ограничен неотрицательными значениями.

Отделяет найденный/не найденный флаг от позиции. Позиция предоставляется как параметр out - существенное другое возвращаемое значение. Он также может быть параметром in-out, чтобы начать поиск из заданной позиции. Использование -1 для указания не найденного не будет разрешено здесь, поскольку оно нарушает проверки диапазона по типу Positive.

Альтернативы в java:

  • выдает исключение: здесь не очень хороший выбор, так как не найти символ не является исключительным условием.
  • разделить результат на несколько методов, например. boolean indexOf(char c); int lastFoundIndex();. Это означает, что объект должен находиться в состоянии, которое не будет работать в параллельной программе, если только состояние не хранится в потоковом локальном хранилище или не используется синхронизация - все значительные накладные расходы.
  • вернуть позицию и найти флаг отдельно: например boolean indexOf(char c, Position pos). Здесь создание объекта позиции можно рассматривать как ненужные служебные данные.
  • создать многозначный тип возврата

таких как

class FindIndex {
   boolean found;
   int position;
}

FindIndex indexOf(char c);

хотя он четко разделяет возвращаемые значения, он страдает от создания объектов. Некоторые из них можно было бы смягчить, передав FindIndex в качестве параметра, например.

FindIndex indexOf(char c, FindIndex start);

Кстати, множественные возвращаемые значения будут частью java (дуб), но были перенесены до 1.0, чтобы сократить время до выпуска. Джеймс Гослинг говорит, он хочет, чтобы они были включены. Это еще желаемая функция.

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

Однако, если использовать магические значения, гораздо удобнее работать, если они согласованы между связанными вызовами api. Например,

   // get everything after the first c
   int index = str.indexOf('c');
   String afterC = str.substring(index);

Java здесь не подходит, так как использование -1 в вызове substring приведет к IndeOutOfBoundsException. Вместо этого было бы более согласованным, если бы подстрока возвращала значение "" при вызове с -1, если отрицательные значения считаются начальными в конце строки. Критики магических значений для условий ошибки говорят, что возвращаемое значение можно игнорировать (или считать положительным). Согласованный api, который использует эти магические значения полезным способом, уменьшит необходимость проверки на -1 и позволит использовать более чистый код.

Ответ 2

Есть -1 магическое число?

В этом контексте не совсем. В -1 нет ничего особенного... кроме того, что он гарантированно является недопустимым значением индекса из-за того, что он отрицательный.

Анти-шаблон?

Нет. Чтобы квалифицироваться как анти-шаблон, в этой идиоме должно быть что-то вредное. Я не вижу ничего вредного в использовании -1 таким образом.

Запах кода?

То же самое. (Возможно, лучший стиль для использования именованной константы, а не голой литерал -1. Но я не думаю, что это то, о чем вы просите, и это не будет считаться "запахом кода", IMO.)

Цитаты и рекомендации от властей

Не то, чтобы я знал. Однако я бы заметил, что это "устройство" используется в различных стандартных классах. Например, String.indexOf(...) возвращает -1, чтобы сказать, что символ или подстрока не удалось найти.


Насколько мне известно, это просто "алгоритмическое устройство", которое полезно в некоторых случаях. Я уверен, что если вы просмотрите литературу, вы увидите примеры использования -1 (или 0 для языков с массивами одного типа) таким образом, начиная с 1960 года и раньше.

Выбор -1 вместо некоторого другого отрицательного числа - это просто вопрос личного вкуса, и (IMO) не стоит анализировать. В этом контексте.


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

Оборотная сторона заключается в том, что если "условие", представленное -1 (или что-то еще), не "ошибка" / "исключительное условие", то возврат специального значения является как разумным, так и собственно.

Ответ 3

Как Java, так и JavaScript используют -1, когда индекс не найден. Поскольку индекс всегда 0-n, это кажется довольно очевидным выбором.

//JavaScript
var url = 'example.com/foo?bar&admin=true';
if(url.indexOf('&admin') != -1){
  alert('we likely have an insecure app!');
}

Я нахожу этот подход (который я использовал при расширении элементов типа Array, чтобы иметь метод .indexOf()), чтобы быть вполне нормальным.

С другой стороны, вы можете попробовать PHP-подход, например. strpos(), но IMHO запутывается, поскольку существует несколько типов возвращаемых значений (он возвращает FALSE, если не найден)

Ответ 4

-1 как возвращаемое значение немного уродливое, но необходимое. Альтернативы сигнализировать о "не найденном" состоянии - ИМХО все хуже:

  • Вы можете выбросить исключение, но это не идеально, потому что Исключения лучше всего использовать для условия, требующие некоторой формы восстановления или распространения. Не обнаружение появления подстроки на самом деле довольно ожидаемый. Также Выброс исключений имеет значительную штраф за производительность.

  • Вы можете использовать составной результат объект с (найденным, индексом), но это требует размещения объекта и более сложный код со стороны вызывающий, чтобы проверить результат.

  • Вы можете выделить два отдельных вызовы функций для contains и indexOf - однако это снова довольно громоздкий для вызывающего а также приводит к результату поскольку оба вызова были бы O (n) и требуют полного обхода Строка.

Лично мне никогда не нравится ссылаться на константу -1: мой тест на не найденный всегда что-то вроде:

int i = someString.indexOf("substring");
if (i>=0) {
  // do stuff with found index
} else {
  // handle not found case
}

Ответ 5

Хорошей практикой является определение конечной переменной класса для константных значений all в вашем коде. Но общепринято использовать 0, 1, -1, "" (пустую строку) без явного объявления.

Ответ 6

Это наследование от C, где может быть возвращено только одно примитивное значение. В java вы также можете вернуть один объект.

Итак, для нового кода возвратите объект базового типа с подтипом, указывающим проблему, которая будет использоваться с instaceof, или выбросите исключение "не найдено".

Для существующих специальных значений make -1 константа в ваших кодовых именах соответственно - NOT_FOUND - поэтому читатель может рассказать значение без необходимости проверять javadocs.

Ответ 7

Та же самая практика, что и для null, относится к -1. Его обсуждали много раз.

например. Java api design - NULL или Exception

Ответ 8

Используется потому, что это первое недопустимое значение, которое вы встречаете в массивах на основе 0. Как вы знаете, не все типы могут содержать нуль или ничего, поэтому нужно "что-то" ничего не значить.

Я бы сказал, что это не официальное, оно только что стало конвенцией (неписаным), потому что оно очень разумно для ситуации. Лично я бы тоже не назвал это проблемой. Дизайн API также зависит от автора, но рекомендации можно найти в Интернете.

Ответ 9

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

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

Теперь я думаю, самый чистый подход состоял бы в том, чтобы иметь метод contains и indexOf, второй из которых будет генерировать исключение, если элемент, о котором вы просите, отсутствует в коллекции. Зачем? Поскольку можно было бы ожидать, что следующее верно:

someCollection.objectAtIndex(someCollection.indexOf(someObject)) == someObject

То, что вы, скорее всего, получите, является исключением, поскольку -1 выходит за пределы, тогда как фактическая причина, почему это правдоподобное отношение неверно, заключается в том, что someObject не является элементом someCollection, и что поэтому внутренний вызов должен вызывать исключение.

Теперь как чистый и надежный, как это может быть, он имеет два ключевых недостатка:

  • Обычно обе операции обычно стоили бы вам O (n) (если у вас нет обратной карты внутри коллекции), поэтому вам будет лучше, если вы сделаете только один.
  • Это действительно довольно многословно.

В конце концов, вам решать. Это вопрос философии. Я бы назвал это "семантическим взломом", чтобы добиться как короткой, так и быстроты за счет надежности. Ваш вызов;)

Greetz
back2dos

Ответ 10

как почему 51% означает все среди акционеров компании, так как она самая лучшая и имеет смысл, а не -2 или -3...