2010-06-13 12 views
12

Duplicar posibles:
Constant abuse?¿Es -1 un número mágico? Un antipatrón? ¿Un olor a código? Cotizaciones y directrices de las autoridades

que he visto -1 utilizado en varias API, más comúnmente cuando se busca en una "colección" con índices basados ​​en cero, generalmente para indicar el índice "no encontrado". Esto "funciona" porque -1 nunca es un índice legal para comenzar. Parece que cualquier número negativo debería funcionar, pero creo que casi siempre se usa -1, como algún tipo de convención (¿no escrita?).

Me gustaría limitar el alcance a Java al menos por ahora. Mis preguntas son:

  • ¿Cuáles son las palabras oficiales de Sun con respecto al uso de -1 como valor de retorno "especial" como este?
  • Qué citas hay con respecto a este problema, por ej. James Gosling, Josh Bloch, o incluso otras figuras autorizadas fuera de Java?
  • ¿Cuáles fueron algunas de las discusiones más notables sobre este tema en el pasado?
+1

posible duplicado de [abuso constante?] (Http: // stackoverflow.com/questions/1862593/constant-abuse). Esta pregunta discute cero de manera explícita pero 0 y -1 son tan comunes en muchos idiomas ... – gbn

+2

Simplemente hágase un 'final int NOT_FOUND = -1;' y siga su camino feliz. Aparte de eso, ¿cuál es la desventaja de hacerlo de forma "-1"? –

+1

@gbn: No veo nada de Josh Bloch en esa pregunta, así que no, no es una tontería. Y tampoco veo cómo esto es subjetivo/argumentativo. Las autoridades dicen una cosa u otra. Los libros prescriben de una manera u otra. Estos son los hechos que estoy buscando. – polygenelubricants

Respuesta

11

Esta es una expresión común en los idiomas en los que los tipos no incluyen controles de rango. Un valor de "fuera de límites" se usa para indicar una de varias condiciones. Aquí, el valor de retorno indica dos cosas: 1) fue el carácter encontrado, y 2) dónde se encontró. El uso de -1 para not found y un índice no negativo para found codifica de forma sucinta estos dos en un valor, y el hecho de que not-found no necesita devolver un índice.

En un lenguaje con estricta verificación de rango, tales como Ada o Pascal, el método puede implementarse como (pseudo código)

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

Positive es un subtipo de int, pero restringido a valores no negativos.

Esto separa la bandera encontrada/no encontrada de la posición. La posición se proporciona como un parámetro de salida, esencialmente otro valor de retorno. También podría ser un parámetro de entrada/salida para iniciar la búsqueda desde una posición determinada. El uso de -1 para indicar no encontrado no estaría permitido aquí ya que viola las verificaciones de rango en el tipo Positivo.

Las alternativas en Java son:

  • lanzar una excepción: esta no es una buena opción aquí, ya que no encontrar un personaje no es una condición excepcional.
  • divide el resultado en varios métodos, p. boolean indexOf(char c); int lastFoundIndex();. Esto implica que el objeto debe aferrarse al estado, que no funcionará en un programa simultáneo, a menos que el estado se almacene en un almacenamiento local de subprocesos o se use sincronización, todos los gastos generales considerables.
  • devuelva la posición y encuentre el indicador por separado: como boolean indexOf(char c, Position pos). Aquí, crear el objeto de posición puede verse como una sobrecarga innecesaria.
  • crear un tipo de retorno de valores múltiples

como

class FindIndex { 
    boolean found; 
    int position; 
} 

FindIndex indexOf(char c); 

aunque separa claramente los valores de retorno, sufre sobrecarga de creación de objetos. Algo de eso podría mitigarse pasando el FindIndex como parámetro, p.

FindIndex indexOf(char c, FindIndex start); 

Incidentalmente, múltiples valores de retorno iban a ser parte de java (roble), pero fueron despedido antes de 1.0 a reducir el tiempo de su liberación. James Gosling says desearía haber sido incluidos. Sigue siendo un wished-for feature.

Mi opinión es que el uso de valores mágicos es una forma práctica de codificar resultados de múltiples valores (un indicador y un valor) en un único valor de retorno, sin requerir excesiva sobrecarga de creación de objetos.

Sin embargo, si usa valores mágicos, es mucho mejor trabajar con ellos si son consistentes en todas las llamadas api relacionadas.Por ejemplo,

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

Java insuficiente a este respecto, ya que el uso de -1 en la llamada a substring causará un IndeOutOfBoundsException. En cambio, podría haber sido más consistente para la subcadena devolver "" cuando se invoca con -1, si se considera que los valores negativos comienzan al final de la cadena. Los críticos de los valores mágicos para las condiciones de error dicen que el valor de retorno se puede ignorar (o suponer que es positivo). Una API apta que maneje estos valores mágicos de una manera útil reduciría la necesidad de verificar -1 y permitir un código más limpio.

+0

+1 para el enfoque de valor de retorno compuesto. muy limpio, aunque agrega una sobrecarga en rendimiento y código. también es un poco engorroso, si realmente sabe que el valor está en la colección. – back2dos

+0

¿Por qué 'indexOf()' aceptar 'FindIndex' como * argumento *? ¿Qué significa indicar que el índice de inicio "no se encuentra"? – seh

+0

También, el estado de la técnica para algo como 'FindIndex': el tipo' Maybe' de Haskell, el tipo 'Option' de Scala, y cualquier otro modelo para monadic zero en la mónada" maybe/optional ". Incluso un puntero o una referencia potencialmente nula a un número entero cuenta. – seh

3

Tanto Java como JavaScript usan -1 cuando no se encuentra un índice. Como el índice siempre es 0-n, parece una elección bastante obvia.

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

encontrar este enfoque (que he utilizado cuando se extiende elementos de tipo matriz de tener un método .indexOf()) a ser bastante normal.

Por otro lado, puede probar el enfoque PHP, p. Ej. strpos() pero en mi humilde opinión se vuelve confuso, ya que hay varios tipos de retorno (devuelve FALSE cuando no encontrado)

+1

También es cierto en .NET. – Richard

+1

Sí, es extremadamente común. Todavía no estoy seguro de que realmente lo haga bien. :) – back2dos

+0

Siendo común, por lo tanto el "status quo", lo hace familiar. Prefiero ir con lo que otros desarrolladores están "esperando" que intentar reinventar la rueda. p.ej. Espero que los índices sean basados ​​en cero, index-not-found == -1, una implementación del método toString() en cada objeto, etc. – scunliffe

1

Es una buena práctica para definir una variable de clase final para todos los valores constantes en su código. Pero se acepta generalmente usar 0, 1, -1, "" (cadena vacía) sin una declaración explícita.

1

Esto es una herencia de C donde solo se puede devolver un único valor primitivo. En java también puedes devolver un solo objeto.

Por lo tanto, para el nuevo código, devuelva un objeto de un tipo de línea con el subtipo que indica el problema que se utilizará con el instaceof, o ejecute una excepción "no encontrado".

Para los valores especiales existentes, haga que -1 sea una constante en los nombres de sus códigos en consecuencia - NO ENFOQUE - para que el lector sepa el significado sin tener que verificar los javadocs.

1

Se usa porque es el primer valor no válido que se encuentra en las matrices basadas en 0. Como sabe, no todos los tipos pueden contener nulos o nada, por lo que necesitan "algo" para no significar nada.

Yo diría que no es oficial, se acaba de convertir en una convención (no escrita) porque es muy sensato para la situación. Personalmente, tampoco lo llamaría un problema. El diseño API también depende del autor, pero guidelines can be found online.

1

Hasta donde yo sé, tales valores se llaman valores centinela, aunque las definiciones más comunes difieren ligeramente de este escenario.

Los lenguajes como Java eligieron no admitir pasar por referencia (lo que creo que es una buena idea), por lo que aunque los valores de los argumentos individuales son mutables, las variables pasadas a una función no se verán afectadas. Como consecuencia de esto, solo puede tener un valor de retorno de un solo tipo. Entonces, lo que debes hacer es elegir un valor de otro tipo que no sea válido y devolverlo para transportar semántica adicional, porque el valor de retorno no es realmente el valor de retorno de la operación, sino una señal especial.

Ahora supongo que el enfoque más limpio sería tener un contains y un método indexOf, el segundo de los cuales arrojaría una excepción, si el elemento que está solicitando no está en la colección. ¿Por qué? Debido a que uno esperaría que lo siguiente es cierto:

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

Lo que es muy probable que conseguir es una excepción porque -1 está fuera de límites, mientras que la razón real de por qué esta relación plausible, no es cierto es que es someObject no es un elemento de someCollection, y es por eso que la llamada interna debería generar la excepción.

Ahora lo más limpio y robusto, ya que esto puede ser, tiene dos defectos principales:

  • Por lo general, ambas operaciones se suelen costar O (n) (a menos que tenga un mapa inversa dentro de la colección), así que estás mejor si haces solo uno.
  • Es realmente bastante detallado.

Al final, depende de usted decidir. Esta es una cuestión de filosofía. Lo llamaría un "truco semántico" para lograr la velocidad & de brecha a costa de la solidez. Su llamada;)

greetz
back2dos

1

como por qué el 51% significa todo entre los accionistas de una empresa, ya que es la mejor y la más cercana tiene sentido en lugar de -2 o -3 ...

4

¿Es -1 un número mágico?

En este contexto, realmente no. No hay nada especial acerca de -1 ... aparte del hecho de que se garantiza que es un valor de índice inválido en virtud de ser negativo.

¿Antipatrón?

No.Para calificar como antipatrón tendré que haber algo dañino en este idioma. No veo nada dañino al usar -1 de esta manera.

¿Un olor a código?

Ditto. (Podría decirse que es mejor estilo usar una constante con nombre en lugar de un literal -1 desnudo. Pero no creo que sea eso lo que está preguntando, y de todos modos no contaría como "olor a código", IMO)

Cotizaciones y directrices de las autoridades

No es que yo sepa. Sin embargo, me gustaría observar que este "dispositivo" se usa en varias clases estándar. Por ejemplo, String.indexOf(...) devuelve -1 para decir que no se pudo encontrar el carácter o la subcadena.


Por lo que a mí respecta, esto es simplemente un "dispositivo algorítmico" que es útil en algunos casos. Estoy seguro de que si volvías la vista atrás de la literatura, verás ejemplos de cómo usar -1 (o 0 para idiomas con matrices basadas en uno) de esta manera desde los años 60 y anteriores.

La elección de -1 en lugar de algún otro número negativo es simplemente una cuestión de gusto personal, y (IMO) no vale la pena analizar., En este contexto.


puede ser una mala idea para un método para volver -1 (o algún otro valor) para indicar un error en lugar de lanzar una excepción. Sin embargo, el problema aquí no es el valor devuelto, sino el hecho de que el método requiere que la persona que llama probe explícitamente los errores.

La otra cara es que si la "condición" representado por -1 (o lo que sea) es no un "error"/"condiciones excepcionales", a continuación, devolver el valor especial es razonable y apropiada.

2

-1 como valor de retorno es ligeramente feo pero necesario. Las alternativas para indicar una condición de "no encontrado" en mi humilde opinión son todos mucho peor:

  • que podría lanzar una excepción, pero esto no es ideal porque las excepciones son los más utilizados para señalar inesperados condiciones que requieren algún forma de recuperación o falla propagada. No encontrar una aparición de una subcadena es realmente bastante esperado. También Excepción de lanzamiento tiene una importante penalización de rendimiento .

  • Se puede usar un objeto de resultado compuesto con (que se encuentra, índice), pero esto requiere una asignación objeto y el código más complejas por parte de la persona que llama para inspeccionar el resultado.

  • Usted puede separar dos por separado función exige contiene y indexOf - sin embargo esto es de nuevo bastante engorroso para la persona que llama y también se traduce en un impacto de rendimiento ya que tanto las llamadas sería O (n) y requieren un recorrido completo de la cadena .

Personalmente, no me gusta para referirse a la constante -1: mi prueba de que no se encuentra, es siempre algo así como:

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