2009-05-18 24 views
6

Acabo de encontrar algo que llamaría una peculiaridad en F # y me gustaría saber si es por diseño o por error, y si es por diseño, ¿por qué es así? .. Si escribe cualquier expresión de rango donde el primer término es mayor que el segundo término, la secuencia devuelta estará vacía. Una mirada al reflector sugiere que esto es por diseño, pero realmente no puedo encontrar una razón por la que debería ser así. Un ejemplo de reproducir que es:Rangos A a B donde A> B en F #


[1..10] |> List.length 
[10..1] |> List.length 

La primera imprimirá 10 mientras que el segundo se imprimirá 0. Las pruebas se realizaron en F # CTP 1.9.6.2.

EDIT: gracias por sugerir explicitar el rango, pero todavía hay un caso (que es lo que me inspiró a hacer esta pregunta) que no será cubierto. ¿Qué pasa si A y B son variables y ninguna es constantemente mayor que la otra, aunque siempre son diferentes? Teniendo en cuenta que la expresión de rango no parece optimizarse en tiempo compilado de todos modos, ¿hay alguna buena razón para que el código que determina el paso (no especificado explícitamente) en el caso A y B no permitan pasos negativos?

+1

Pregunta ligeramente relacionada con algún código que pueda ser de interés http://stackoverflow.com/questions/377078/f-floating-point-ranges-are-experimental-and-may-be-deprecated – Benjol

Respuesta

10

Según lo sugerido por otras respuestas, puede hacerlo

[10 .. -1 .. 1] |> List.iter (printfn "%A") 

por ejemplo,

[start .. step .. stop] 
+0

Gracias por esto, pero ¿podrían ver/comentar/responder mi edición? Gracias de antemano – em70

+0

Siempre puede escribir su propia función de argumento dos (o tres) para hacer lo que quiera. La sintaxis abreviada es, creo, utilizada el 98% del tiempo para los casos en que todos los valores son valores literales. Al final del día, creo que todas las opciones posibles de diseño del lenguaje en este espacio son razonables y nadie se destaca como objetivamente mejor que otros en mi opinión. – Brian

+0

Ya he escrito mi propia función, pero la idea de que el paso negativo determinado automáticamente esté bloqueado explícitamente me molesta, considerando que sería bastante intuitivo que una sintaxis indicara un rango para admitir la creación de rangos en ambos direcciones. – em70

1

Los rangos se expresan en general (en las lenguas y los marcos que los apoyan) así:

low_value <to> high_value

¿Puede dar un buen argumento de por qué un rango debe ser capaz de expresarse ¿diferentemente? Dado que usted estaba solicitando un rango de un número más alto a un número más bajo, ¿no es razonable que el rango resultante no tenga miembros?

+0

El caso en el que A y B son variables, es un caso bastante bueno en mi opinión ... – em70

2

En haskell, puede escribir [10, 9 .. 1]. Quizás funciona igual en F # (no lo he probado)?

edición:

parece que la sintaxis # F es diferente, tal vez algo como [10..-1..1]

3

lo que "debe" suceder es, por supuesto, subjetiva. La notación de rango normal en mi mente define [x..y] como el conjunto de todos los elementos mayores o iguales a xY menores que o iguales a y; un conjunto vacío si y < x. En este caso, debemos apelar a la especificación de F #.

Las expresiones de rango expr1 .. expr2 se evalúan como una llamada al operador sobrecargado (..), cuyo enlace predeterminado se define en Microsoft.FSharp.Core.Operators. Esto genera un IEnumerable < _> para el rango de valores entre los valores de inicio (expr1) y final (expr2) dados, usando un incremento de 1. El operador requiere la existencia de un miembro estático (..) (nombre largo GetRange) en el tipo estático de expr1 con una firma apropiada.

Las expresiones de rango expr1 .. expr2 .. expr3 se evalúan como una llamada al operador sobrecargado (.. ..), cuyo enlace predeterminado se define en Microsoft.FSharp.Core.Operators. Esto genera un IEnumerable < _> para el rango de valores entre los valores de inicio dados (expr1) y final (expr3), usando un incremento de expr2. El operador requiere la existencia de un miembro estático (..) (nombre largo GetRange) en el tipo estático de expr1 con una firma apropiada.

El estándar no parece definir el operador .. (al menos, que puedo encontrar).Pero deberías poder cambiar el enlace de los tipos que te interesan para que se comporten de la forma que desees (incluida la cuenta regresiva si x> y).

3

Adam Wright - Pero usted debería ser capaz que cambiar el enlace para este tipo de estás interesado en que se comporten de forma que como (incluyendo la cuenta atrás si x> y).

Tomando la sugerencia de Adam en código:

let (..) a b = 
    if a < b then seq { a .. b } 
      else seq { a .. -1 .. b } 

printfn "%A" (seq { 1 .. 10 }) 
printfn "%A" (seq { 10 .. 1 }) 

Esto funciona para los rangos int. Eche un vistazo al código fuente de (..): puede usarlo para trabajar sobre otros tipos de rangos, pero no está seguro de cómo obtendrá el valor correcto de -1 para su tipo específico.

Cuestiones relacionadas