2012-05-16 10 views
12

El proyecto que uso contiene muchas clases heredadas de una sola clase base. En pruebas unitarias necesito comparar los resultados recibidos por tipos y datos.El compilador de F # arroja OutOfMemoryException

Cuando uso coincida con comparación por tipos en el caso en que la lista de condiciones contenga suficientes condiciones diferentes para el compilador arroje OutOfMemoryException.

Por ejemplo, después de código F # plantea System.OutOfMemoryException (FS0193 error de parámetro) durante la compilación (y compilación tomó unos 30 segundos antes fue arrojado excepción)

type IBaseClass() = class end 

type IChildClass1() = inherit IBaseClass() 

type IChildClass2() = inherit IBaseClass() 

type IChildClass3() = inherit IBaseClass() 

type IChildClass4() = inherit IBaseClass() 

type IChildClass5() = inherit IBaseClass() 

type IChildClass6() = inherit IBaseClass() 

type IChildClass7() = inherit IBaseClass() 

type IChildClass8() = inherit IBaseClass() 

type IChildClass9() = inherit IBaseClass() 

type IChildClass10() = inherit IBaseClass() 

type IChildClass11() = inherit IBaseClass() 

type IChildClass12() = inherit IBaseClass() 

type IChildClass13() = inherit IBaseClass() 

type IChildClass14() = inherit IBaseClass() 

type IChildClass15() = inherit IBaseClass() 

type IChildClass16() = inherit IBaseClass() 

type IChildClass17() = inherit IBaseClass() 

type IChildClass18() = inherit IBaseClass() 

type IChildClass19() = inherit IBaseClass() 

type IChildClass20() = inherit IBaseClass() 


let AreEqual (original: IBaseClass) (compareWith: IBaseClass) : bool = 
    match (original, compareWith) with 
    | (:? IChildClass1 as a), (:? IChildClass1 as b) -> a = b 
    | (:? IChildClass2 as a), (:? IChildClass2 as b) -> a = b 
    | (:? IChildClass3 as a), (:? IChildClass3 as b) -> a = b 
    | (:? IChildClass4 as a), (:? IChildClass4 as b) -> a = b 
    | (:? IChildClass5 as a), (:? IChildClass5 as b) -> a = b 
    | (:? IChildClass6 as a), (:? IChildClass6 as b) -> a = b 
    | (:? IChildClass7 as a), (:? IChildClass7 as b) -> a = b 
    | (:? IChildClass8 as a), (:? IChildClass8 as b) -> a = b 
    | (:? IChildClass9 as a), (:? IChildClass9 as b) -> a = b 
    | (:? IChildClass10 as a), (:? IChildClass10 as b) -> a = b 
    | (:? IChildClass11 as a), (:? IChildClass11 as b) -> a = b 
    | (:? IChildClass12 as a), (:? IChildClass12 as b) -> a = b 
    | (:? IChildClass13 as a), (:? IChildClass13 as b) -> a = b 
    | (:? IChildClass14 as a), (:? IChildClass14 as b) -> a = b 
    | (:? IChildClass15 as a), (:? IChildClass15 as b) -> a = b 
    | (:? IChildClass16 as a), (:? IChildClass16 as b) -> a = b 
    | (:? IChildClass17 as a), (:? IChildClass17 as b) -> a = b 
    | (:? IChildClass18 as a), (:? IChildClass18 as b) -> a = b 
    | (:? IChildClass19 as a), (:? IChildClass19 as b) -> a = b 
    | (:? IChildClass20 as a), (:? IChildClass20 as b) -> a = b 
    | _ -> false 

Por seguro que puedo añadir a la interfaz IEquatable mi clase IBaseClass que es evitará el uso de tales partido construcción, o añadir int tipo miembro (o enumeración) a la interfaz IBaseClass y hacer partido y no por tipos, pero por alguna valor int.

Nota, he tratado de compilar el mismo proyecto en MS VS 2010 y MSVS 11 Beta, y tenía el mismo error del compilador

Pregunta: ¿Por OutOfMemoryException del compilador sucede en mi caso (se sabe compilador error u otra limitación), ¿cómo debo reorganizar mi condición coincidencia para evitarlo?

actualización Cuando pongo clases discriminadas en sindicatos y uso similar partido comparación Fsc.exe compila el proyecto sin excepción

type AllClasses = 
    | ChildClass1 of IChildClass1 | ChildClass2 of IChildClass2 | ChildClass3 of IChildClass3 | ChildClass4 of IChildClass4 | ChildClass5 of IChildClass5 | ChildClass6 of IChildClass6 
    | ChildClass7 of IChildClass7 | ChildClass8 of IChildClass8 | ChildClass9 of IChildClass9 | ChildClass10 of IChildClass10 | ChildClass11 of IChildClass11 | ChildClass12 of IChildClass12 
    | ChildClass13 of IChildClass13 | ChildClass14 of IChildClass14 | ChildClass15 of IChildClass15 | ChildClass16 of IChildClass16 | ChildClass17 of IChildClass17 | ChildClass18 of IChildClass18 
    | ChildClass19 of IChildClass19 | ChildClass20 of IChildClass20 

let AreEqual2 (original: AllClasses) (compareWith: AllClasses) : bool = 
    match (original, compareWith) with 
    | ChildClass1(a), ChildClass1(b) -> a = b 
    | ChildClass2(a), ChildClass2(b) -> a = b 
    | ChildClass3(a), ChildClass3(b) -> a = b 
    | ChildClass4(a), ChildClass4(b) -> a = b 
    | ChildClass5(a), ChildClass5(b) -> a = b 
    | ChildClass6(a), ChildClass6(b) -> a = b 
    | ChildClass7(a), ChildClass7(b) -> a = b 
    | ChildClass8(a), ChildClass8(b) -> a = b 
    | ChildClass9(a), ChildClass9(b) -> a = b 
    | ChildClass10(a), ChildClass10(b) -> a = b 
    | ChildClass11(a), ChildClass11(b) -> a = b 
    | ChildClass12(a), ChildClass12(b) -> a = b 
    | ChildClass13(a), ChildClass13(b) -> a = b 
    | ChildClass14(a), ChildClass14(b) -> a = b 
    | ChildClass15(a), ChildClass15(b) -> a = b 
    | ChildClass16(a), ChildClass16(b) -> a = b 
    | ChildClass17(a), ChildClass17(b) -> a = b 
    | ChildClass18(a), ChildClass18(b) -> a = b 
    | ChildClass19(a), ChildClass19(b) -> a = b 
    | ChildClass20(a), ChildClass20(b) -> a = b 
    | _ -> false 

Gracias

+0

Supongo que está utilizando 'IChildClass1-20' solo con fines ilustrativos, ¿o es este código real? No creo que el compilador arroje una 'OutOfMemoryException' (pero no tengo la respuesta) – Abel

+0

@Abel - sí, los nombres de clase son solo ilustrativos, las clases tienen nombres diferentes. Nota sobre mi máquina si uso IChildClass1-18 en * AreEqual * - el proyecto es compilable, 19 y más - boom ...excepción – Vitaliy

Respuesta

9

Esto es causado por la forma en la F # compilador compila la coincidencia de patrones en tuplas en este caso. No estoy del todo seguro de cuándo exactamente se ejecuta en este problema en particular y cuando el compilador utiliza otro enfoque, pero aquí hay una explicación de por qué falla en este caso ...

Si escribe un patrón coincidente como el de En su ejemplo, el compilador genera esencialmente un árbol de decisión que prueba el primer patrón para original (:? IChildClass1) y luego genera dos ramas. La primera rama verifica si compareWith también es IChildClass1. Si es así, ejecuta el primer caso. El resto de la coincidencia de patrones es entonces duplicados en las dos ramas, para que pueda obtener algo así como (se puede comprobar esto mirando el código compilado para el más pequeño número de casos utilizando ILSpy):

if (original is IChildClass1) 
    if (compareWith is IChildClass1) 
    case #1 
    if (original is IChildClass2) 
    if (compareWith is IChildClass2) 
     case #2 
    if (original is IChildClass3) 
     (...) 
else 
    if (original is IChildClass2) 
    if (compareWith is IChildClass2) 
     case #2 
    if (original is IChildClass3) 
     (...) 

Este significa que el tamaño del código generado es exponencialmente proporcional al número de casos en este patrón coincidente. En 20 de los casos, el compilador intenta crear 2^20 ramas, lo que (como era de esperar) falla.

+0

gracias por la explicación. sí, entiendo que Fsc crea 2^20 ramas, lo que causa la excepción. Pero si pongo interfaces en uniones discriminadas (escriba AllClasses = ChildClass1 de IChildClass1 | ChildClass2 de IChildClass2 | ChildClass3 de IChildClass3 ...) y use el mismo * match * por tuplas con uniones discriminadas - el compilador compila el proyecto sin problemas - por lo que parece como por alguna razón compilado hace diferentes ramas if-else para comparaciones por tipos y comparaciones con sindicatos discriminados – Vitaliy

+1

@Vitaliy Como dije, no sé exactamente cómo el compilador decide qué hacer. Sin embargo, compilar sindicatos discriminados es bastante diferente a la prueba contra interfaces. En el caso de los DU, el compilador sabe que solo se sostendrá un caso. En el caso de las interfaces, un valor podría coincidir con varios patrones (puede implementar múltiples interfaces). –

+1

LOL. Tuve el mismo error en HLVM. http://flyingfrogblog.blogspot.co.uk/2010/04/variant-types-and-pattern-matching-in.html –

Cuestiones relacionadas