Diseñando el rango perfecto literal

9

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)

  1. Solo puede describir inclusivo y exclusivo para el final. Si bien describe la mayoría de los escenarios, parece un poco inconsistente.

  2. 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 :)

  3. 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

Homde
fuente
2
1,5,10,15,20Brecha 4, 5, 5, 5 ?!
Peter Taylor
oops :) corregido
Homde

Respuestas:

14

¿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?

Dan McGrath
fuente
3
Hola, en realidad, estoy sobre notación de intervalo. Sin embargo, lo que me hizo no querer usarlo fue el hecho de que no era simétrico con ambos [y] y utilizaba la parálisis, además de no tener pasos (como usted menciona). El uso de la parálisis parece tener el potencial de confundir la combinación de llaves y prefiero guardarlas en expresiones. Usar] y [es un estándar iso y parece mejorar con la integración de los pasos, pero esa es mi opinión, por supuesto.
Homde
77
@MKO: su idea de usar paréntesis sin igual (como [1..5[) confundirá a los editores al menos tanto como la notación de intervalo estándar.
David Thornley
2
Hmm, otra idea, ¿qué hay de usar! para, es decir: 1 ..! 5 o 0..5 ..! 20 (con pasos)
Homde
1
@MKO: Usar !para excluir el primer o el último elemento podría ser bueno.
FrustratedWithFormsDesigner
8

¿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 ):

[1,2..10] = [1,2,3,4,5,6,7,8,9,10]
[1,3..10] = [1,3,5,7,9]
[4,3..0]  = [4,3,2,1,0]
[0,5..]   = [0,5,10,15,20,25,30,35...  -- infinite
[1,1..]   = [1,1,1,1,1,1,1,1,1,1,1...  -- infinite

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).

Matthieu M.
fuente
5

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 expresarlos range().

S.Lott
fuente
2

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.

Alexander Gessler
fuente
1
en cuanto a la notación "estándar", las escuelas europeas enseñan [], [[, ]]y ][, sin paréntesis ... y nuestra norma es mejor, por supuesto;)
Matthieu M.
@ Matthieu: En realidad, aquí en los Países Bajos (que está en Europa), también nos enseñaron esa notación estándar.
Fritas
1

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]

Canuteson
fuente
Ese es un punto válido y quizás una cuestión de gustos, simplemente me pareció que sería más fácil pensar "1 paso 5 a 20" en lugar de "del 1 al 2, con el paso 5" usando [1..20] [5] parece un poco desordenado, pero quizás el enfoque de coma sería viable. Agradecería más comentarios sobre eso
Homde
1
  • [1..5 [1,2,3,4
  • ] 1..5] 2,3,4,5
  • ] 1..5 [2,3,4

¿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.

  • [5..1] 5,4,3,2,1

es, por supuesto (1 a MAX) (x => y = (MAX + 1) - x), pero eso es mucho repetitivo y no es fácil de usar.

  • [0..5..20] 0,5,10,15,20

es, por supuesto (0 a 4) (x => y = x * 5) no es tanto repetitivo. Disputable.

usuario desconocido
fuente
El principal beneficio de excluir rangos, afaik, es que el número de elementos es la diferencia de los puntos finales.
Justin L.
@JustinL .: 5-1 es 4 en mi álgebra pero contiene 3 elementos, excluyendo los límites: 2, 3, 4.
usuario desconocido
1

¿Qué pasa con algo como esto?

  • [5..1] 5,4,3,2,1
  • [1..5] [i, e] 1,2,3,4
  • [5..1] [i, e] 5,4,3,2
  • [1..5] [e, i] 2,3,4,5
  • [1..5] [e, e] 2,3,4
  • [0..20] por 5 0,5,10,15,20

El iy een el segundo par de corchetes indica si el comienzo o el final del rango es inclusivo o exclusivo (o podría usarlo incl, y exclsi desea ser más claro). el by 5indica 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 y by 1para 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.

FrustratedWithFormsDesigner
fuente
¿Alguien puede explicar el voto negativo?
FrustratedWithFormsDesigner
No vi nada evidentemente incorrecto . Voté para contrarrestar el voto negativo.
Berin Loritsch
1

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:

0..5           = 0,1,2,3,5
0..5..10       = 0,5,10
..3            = 0,1,3
!0..!5         = 1,2,3,4
3..            = 3 to infinity

segmented ranges
-
0..5,..5..20   = 0,1,2,3,4,5,10,15,20
0..3,10..5..20 = 0,1,2,3,10,15,20

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

Homde
fuente
Parece estar bien excepto cuando se especifica el paso. Creo que sería más limpio como [0..10 por 5]. El rango segmentado podría ser [0..5, ..20 por 5], etc. Uno también podría especificar [numitems desde el primer paso], que (a diferencia de las otras formas) podría permitir un tamaño de paso de cero.
supercat
0

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.

AShelly
fuente
0

Ruby tiene notación y un objeto Range que funciona. Esencialmente se ve así:

1..5  # range 1,2,3,4,5

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:

(1..5).step(3) { |x| puts x }
(1..5).step(2) { |x| puts x }

Así que aquí hay cosas que puede considerar:

  • Agregar soporte de idiomas para puntos finales inclusivos / exclusivos puede ser problemático. Si todos los rangos son inclusivos (elección de Ruby), los desarrolladores ajustarán los puntos finales del rango en consecuencia. ¿Cómo representa la inclusión o exclusividad de los puntos finales de una manera que sea visualmente inequívoca e inequívoca con una gramática de analizador simple?
  • ¿Estás usando el rango para algo más que iterar? Los ejemplos comunes incluyen comparaciones de rango, empalme, valor en comparaciones de rango. Si es así, ¿cómo representa esas posibilidades? (Consulte el enlace a la clase Range para obtener ideas sobre cada uno de estos ejemplos).

Por cierto, los rangos son bastante agradables si permite que se incluyan en una declaración de cambio como lo hace Ruby:

switch(myval)
    when 0..33 then puts "Low"
    when 34..66 then puts "Medium"
    when 67..100 then puts "High"
end

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.

Berin Loritsch
fuente
Lea la pregunta detenidamente, OP ya conoce los rangos de Ruby: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).
FrustratedWithFormsDesigner
0

Una solución que sin duda consideraría es utilizar la sobrecarga del operador para permitir, por ejemplo,

1+[0..3]*5

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.

Peter Taylor
fuente
2
Muy ambiguo list expression * 5También podría significar "repetir el valor de list expression5 veces", como lo hace en Python.
nikie
Esto es muy extraño buscando y no estoy seguro de lo que sería ... 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 ...
FrustratedWithFormsDesigner