He estado pensando en cómo diseñar el rango literal "perfecto" si tuviera que diseñar un lenguaje. Para usted que no sabe, conozca un rango literal en una declaración que represente un rango de valores, como 1-4. Se usan más comúnmente en bucles for / foreach
Parece que hay un par de problemas que uno debe tener en cuenta
La compatibilidad con rangos inclusivos y exclusivos, agregar +1 o -1 a los puntos finales parece un poco fugosa y propensa a errores.
Soporte para pasos, por lo que puede hacer un rango de números pares o impares, por ejemplo
Legibilidad, debería ser evidente lo que describe el rango literal
No ambigüedad, debe ser perfectamente inequívoco lo que describe el literal de rango
El valor predeterminado probablemente debería ser de inclusivo a exclusivo, ya que eso es lo que se usa en la mayoría de los casos para realizar un bucle sobre matrices, etc.
De todos modos, un ejemplo de rango literal que he visto es Ruby, que tiene la forma de 1..3 para un rango exclusivo (al final) y 1 ... 3 para incluir (al final). También puedes hacer 1..10.paso (5). Después de una cuidadosa consideración, sin embargo, encontré un par de cosas que no me gustaron de ese enfoque (de mi conocimiento limitado de ruby)
Solo puede describir inclusivo y exclusivo para el final. Si bien describe la mayoría de los escenarios, parece un poco inconsistente.
Variando por solo un adicional. parece una receta para dificultar ver si un rango es inclusivo o exclusivo. No sé sobre ti, pero los puntos tienden a convertirse en algo borroso :)
Agregar un método como la notación para rangos parece mezclar la noción de un literal con la de una clase que parece un poco inconsistente (incluso si los rangos se compilan en una clase)
De todos modos, después de reflexionar sobre diferentes alternativas. Se me ocurrió esto
- [5..1] 5,4,3,2,1
- [1..5 [ 1,2,3,4
- ] 1..5] 2,3,4,5
- [ 0..5..20] 0,5,10,15,20
Etcétera. Me gusta porque [normalmente denota un conjunto y esto encaja un poco en eso, a pesar de que esto en contraste con un conjunto sería ordenado.
Sin embargo, una cosa que me preocupa un poco es hacer que los indicadores exclusivos / inclusivos sean obligatorios o no, es decir, si escribe solo 1..5, el valor predeterminado sería 1,2,3,4 ya que es el caso más común con matrices, etc. Es más fácil y más legible, pero menos específico y si tuvieras que escribir [1..5 [aprenderás temprano sobre cómo funcionan.
Entonces, ¿qué piensas, cubrí la mayoría de las bases, pasé por alto algo? ¿Harías obligatorio el []? ¿Diseñarías los literales de rango de manera diferente en tu lenguaje de programación?
Candidatos
- estilo de soporte: [0..10 [ , con paso: [0..5..20 [
- notación de intervalo: [0..10) con paso: [0..5..20)
- exclamación por exlusivo. 0 ..! 10, con paso: 0..5 ..! 20
- Con diferente paso. 0 ..! 20, 5
- sin embargo, eso haría que el valor predeterminado * 0..10 ' inclusive-inclusive
- prolijo: [0 a! 20 por 5]
Debo decir que mi favorito estéticamente hasta ahora es 0 ..! 10 y 0..5 ..! 20 , solo deseo que el 0..10 predeterminado a inclusivo-exclusivo sea más lógico
1,5,10,15,20
Brecha 4, 5, 5, 5 ?!Respuestas:
¿Por qué inventar lo que ya existe en Matemáticas?
Notación de intervalos
Eso debería cubrir su punto de 'No ambigüedad' como ya existe.
Su único problema sería definir los pasos. ¿Quizás algo más en matemáticas puede ayudar con eso?
fuente
[1..5[
) confundirá a los editores al menos tanto como la notación de intervalo estándar.!
para excluir el primer o el último elemento podría ser bueno.¿Has visto las gamas Haskell? Su idea de pasos es similar a su (en el medio) pero indicada con una diferencia sintáctica (de la respuesta de @ luqui ):
Esta notación me parece muy intuitiva: comienzas a enumerar y saltas el cuerpo para marcar dónde debe terminar.
No cubre el caso de "exclusivo", pero, francamente, prefiero tener claro el comportamiento una vez (especifique qué límite es inclusivo y cuál es exclusivo), y espera que el usuario realice ajustes a menos que la sintaxis sea extremadamente clara (y no confunde editores agnósticos de lenguaje).
fuente
Debe leer sobre las comprensiones de listas en los idiomas que las ofrecen.
Python, por ejemplo, cubre sus casos bastante bien con la
range()
función y la comprensión de la lista para casos demasiado complejos para expresarlosrange()
.fuente
Creo que tu notación está lejos de ser inequívoca. Intuitivamente,
[0..5..20]
no me parece un rango con ancho de paso = 5. Incluso falla en algunos casos de esquina: ¿qué pasa con la expresión del conjunto (0,2),[0..2..2]
o qué debo poner en el medio?Creo que la notación de corte de Python es un enfoque sólido:
[start:end:step]
(el paso es opcional).Si realmente necesita soporte para rangos exclusivos e inclusivos, usaría la notación de rango estándar (es decir, usar
(
y[
) a menos que esto presente ambigüedades sintácticas en su idioma.fuente
[]
,[[
,]]
y][
, sin paréntesis ... y nuestra norma es mejor, por supuesto;)Creo que cuando establece un tercer valor de incremento, es mejor agregarlo al final, en lugar de al medio, ya que este es un valor opcional y parece más intuitivo para los programadores experimentados que agregar un tercer argumento opcional en el medio .
así que en lugar de [1..5..20] podrías hacer algo como [1..20] [5] o [1..20,5]
fuente
¿Por qué no usar [1..4], [2..5], [2..4]? Excluir los rangos no ofrece muchos beneficios. No necesita escribir MIN + 1 o MAX-1, sí, pero de todos modos no lo prohíben. Demasiada variación, en mi humilde opinión. Mi lenguaje de elección, scala, tiene (1 a 3) y (1 a 3) [= (1 a 2)] pero al principio me confundió. Ahora siempre uso 'x to y' y sé, por lo tanto, que la opción que no uso es la excluyente, pero por supuesto no me beneficio de una opción que no uso.
es, por supuesto (1 a MAX) (x => y = (MAX + 1) - x), pero eso es mucho repetitivo y no es fácil de usar.
es, por supuesto (0 a 4) (x => y = x * 5) no es tanto repetitivo. Disputable.
fuente
¿Qué pasa con algo como esto?
El
i
ye
en el segundo par de corchetes indica si el comienzo o el final del rango es inclusivo o exclusivo (o podría usarloincl
, yexcl
si desea ser más claro). elby 5
indica el intervalo de paso. El primer y último ejemplo incluyen el primer y el último valor, por lo que omití el[i,i]
, que creo que sería un comportamiento predeterminado asumido.Los valores predeterminados podrían ser
[i,i]
para el especificador inclusivo / exclusivo yby 1
para el especificador de pasos.Normalmente hubiera sugerido la notación estándar que involucra
(
y[
como mencionó @Dan McGrath, pero estoy de acuerdo en que en el código esto podría parecer confuso.fuente
Y el ganador es
Perdón por elegir mi propia solución, realmente aprecio todos los comentarios y comentarios y critique esta solución si encuentra algo mal en ella.
Entonces decidí ir con:
Como puede ver, el valor predeterminado en ambos sentidos es inclusivo, es lo que tiene más sentido intuitivo, especialmente cuando se usa el paso a paso. Puedes usar ! para hacer un final exclusivo.
La desventaja es que normalmente desea usar exclusivo cuando trabaje contra una matriz, pero, de nuevo, la mayoría de las veces probablemente debería usar algo como para cada uno en esos casos. Si realmente necesita trabajar contra el índice, el analizador podría reconocer cuándo posiblemente olvidó el marcador de exclusión al final y emitir una advertencia
Seguí usando ... en ambos lados de la variable de paso en lugar de usar comas o cualquier otra cosa, ya que eso es lo que parecía más limpio y espero que sea el más legible.
También agregué una sintaxis con comas para poder hacer rangos donde diferentes partes tienen diferentes pasos
fuente
No usaría el formulario '[1..5 [' por una de las mismas razones por las que evitaría mezclar parens y llaves: confundiría la mayoría de las llaves existentes de los editores. Quizás use un marcador dentro de las llaves, como '[1 .. | 5]'.
La otra cosa a considerar es el comportamiento de los métodos para obtener los límites. Por ejemplo, 'begin' / 'end' vs. 'first' / 'last' vs. 'min' / 'max'. Deben ser sanos tanto para límites inclusivos como exclusivos, así como para aquellos con un tamaño de paso.
fuente
Ruby tiene notación y un objeto Range que funciona. Esencialmente se ve así:
Con Ruby, el tamaño del paso no es una función del rango, sino una función de iterar a través del rango. Podríamos usar el mismo rango anterior e iterarlo de manera diferente si quisiéramos:
Así que aquí hay cosas que puede considerar:
Por cierto, los rangos son bastante agradables si permite que se incluyan en una declaración de cambio como lo hace Ruby:
No estoy diciendo que el objeto de rango de Ruby sea el final de todo y sea todo, pero es una implementación funcional del concepto del que estás hablando. Juegue con él para ver qué le gusta, qué no le gusta y qué haría de manera diferente.
fuente
Anyways, one example of range literal I've seen is Ruby which is in the form of 1..3 for an exclusive (on the end) range and 1...3 for inclusive (on the end). You can also do 1..10.step(5).
Una solución que sin duda consideraría es utilizar la sobrecarga del operador para permitir, por ejemplo,
No sería necesariamente transparente de inmediato para todos, pero una vez que se detengan y piensen, espero que la mayoría de los programadores lo entiendan, y las personas que usan el idioma con regularidad simplemente lo aprenderían como un idioma.
Para aclarar, el resultado sería
[1, 6, 11, 16]
levantar la multiplicación y luego la suma sobre el rango. No me había dado cuenta de que podría ser ambiguo para los usuarios de Python; Pienso en los rangos como subconjuntos de pedidos totales en lugar de como listas. y repetir un set no tiene sentido.fuente
list expression * 5
También podría significar "repetir el valor delist expression
5 veces", como lo hace en Python.1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3
?1,3,6,9,12
?5,10,15
? algo más, tal vez? De cualquier manera, encuentro esta notación completamente contraintuitiva. Parece que es una especie de extraña operación de puntero C ...