2009-06-17 8 views
10

Si bien jugar con D 2.0 He encontrado el siguiente problema:Cómo utilizar pura en D 2.0

Ejemplo 1:

pure string[] run1() 
{ 
    string[] msg; 
    msg ~= "Test"; 
    msg ~= "this."; 
    return msg; 
} 

Esto compila y funciona como se esperaba.

Cuando trato de envolver la matriz de cadenas en una clase Encuentro que no puedo conseguir que esto funcione:

class TestPure 
{ 
    string[] msg; 
    void addMsg(string s) 
    { 
     msg ~= s; 
    } 
}; 

pure TestPure run2() 
{ 
    TestPure t = new TestPure(); 
    t.addMsg("Test"); 
    t.addMsg("this."); 
    return t; 
} 

Este código no se compilará porque la función addMsg es impuro. No puedo purificar esa función ya que altera el objeto TestPure. ¿Me falta algo? ¿O es esto una limitación?

La siguiente se compila:

pure TestPure run3() 
{ 
    TestPure t = new TestPure(); 
    t.msg ~= "Test"; 
    t.msg ~= "this."; 
    return t; 
} 

¿No sería el operador = ~ sido implementado como una función impura de la matriz msg? ¿Cómo es que el compilador no se queja de eso en la función run1?

+0

He estado tratando de limpiar la etiqueta [tag: pure], porque a veces se refiere a funciones virtuales puras, a veces a [puro] (http://beebole.com/pure/) y, a veces a [puro] (http://en.wikipedia.org/wiki/Pure_ (programming_language)) - entre otros. Pero no sé nada sobre [tag: d2]. ¿Podría confirmar si la edición de mi etiqueta es apropiada? ¿Funcionaría [tag: puramente funcional] para esta pregunta? Creé [tag: pure-function], así que si [tag: pure-functional] funciona, creo que sería mejor usar la etiqueta existente. –

Respuesta

6

Desde v2.050, D relajó la definición de pure para aceptar también las llamadas funciones "débilmente puras". Esto se refiere a las funciones que "do not read or write any global mutable state". Las funciones débilmente puras son no las mismas como funciones puras en el sentido del lenguaje funcional. La única relación es que hacen funciones puros reales, a.k.a. funciones "fuertemente puras" que pueden llamar a las débiles, como el ejemplo de OP.

Con esto, addMsgpuede marcado como (débilmente) pure, ya que sólo la variable local this.msg se altera:

class TestPure 
{ 
    string[] msg; 
    pure void addMsg(string s) 
    { 
     msg ~= s; 
    } 
}; 

y, por supuesto, ahora se puede utilizar el (muy) pure función run2 sin modificaciones

pure TestPure run2() 
{ 
    TestPure t = new TestPure(); 
    t.addMsg("Test"); 
    t.addMsg("this."); 
    return t; 
} 
+1

Muy bueno. Es curioso cómo no hay una buena manera de lidiar con las respuestas que se desactualizan en el desbordamiento de la pila. –

+0

espera, las funciones débilmente puras pueden modificar variables de instancia de clase? En este caso, ¿no está el mensaje fuera del alcance de la función "addMsg" y, por lo tanto, no se puede modificar? Si esto es posible, ¿qué hay de malo en escribir una función pura o delegar dentro de otra función, que modifica el estado de esa función? –

+0

@Andrew: no está fuera del alcance. Hay un parámetro implícito 'this' en las funciones miembro, y' msg' es accesible desde 'this'. – kennytm

3

Por favor revise la definición de funciones puras:

funciones puras son las funciones que producen el mismo resultado para los mismos argumentos. A tal efecto, una función pura:

  • tiene parámetros que son todos invariante o son convertir implícitamente a invariantes
  • no lee o escribe cualquier estado mutable mundial

Uno de los efectos de usar funciones puras es que se pueden paralelizar de forma segura. Sin embargo, no es seguro ejecutar varias instancias de su función en paralelo, ya que ambas podrían modificar la instancia de la clase simultáneamente, causando un problema de sincronización.

+0

"ambos podrían modificar la instancia de clase simultáneamente" ... ¿qué? ¿cómo? – hasen

+0

auto tp = new TestPure; void threadFunc() {tp.addMsg ("Hola mundo"!); } nuevo Thread (& threadFunc); nuevo Thread (& threadFunc); Es imposible garantizar que los dos subprocesos que ejecutan threadFunc no intenten escribir en la instancia de tp al mismo tiempo. Por lo tanto, addMsg no puede ser puro. –

+0

Ugh ... Por encima del comentario, excepto que no se han estropeado juntos: http://dump.thecybershadow.net/ca761137c80da1cee3f2657b215f758d/00000039.txt –

-1

Simplemente una corazonada, pero esta función no siempre devuelve el mismo resultado.

Ver, devuelve una referencia a algún objeto, y aunque el objeto siempre contendrá los mismos datos, los objetos devueltos por varias llamadas a las mismas funciones no son idénticos; es decir, no tienen la misma dirección de memoria.

Cuando devuelve una referencia al objeto, esencialmente devuelve una dirección de memoria, que será diferente en varias llamadas.

Otra forma de pensarlo, parte del valor de retorno es la dirección de memoria de un objeto, que depende de algunos estados globales, y si la salida de una función depende del estado global, entonces no es puro. Demonios, ni siquiera tiene que depender de eso; mientras una función lea un estado global, entonces no es puro. Al llamar "nuevo", estás leyendo estado global.

+0

Su lógica implica que las funciones puras solo deberían poder devolver los tipos de valores que caben en los registros del procesador ... –

+0

Lo siguiente se compila y muestra que se devuelven diferentes objetos para cada llamada. pure int * test_pure2() { int * p = new int; * p = 42; return p; } int * i1 = test_pure2(); int * i2 = test_pure2(); writeln (i1, i2); –

+0

La función pure devolverá diferentes instancias del mismo valor. Es obvio que los punteros no pueden ser idénticos para las dos instancias. Creo que está claro lo que se quiere decir aquí: no debes usar las direcciones de los resultados (de lo contrario, mira mi comentario anterior). –

0

I piensa que su código es conceptualmente correcto.Sin embargo, es posible que haya encontrado un caso donde el análisis semántico del compilador no es tan bueno como el de su cerebro.

Considere el caso donde la fuente de la clase no está disponible. En esos casos, el compilador no tendría manera de decir que addMsg solo modifica la variable miembro por lo que no puede permitirte llamar desde una función pura.

Para permitir que en su caso, tendría que tener manejo de casos especiales para este tipo de uso. Cada regla de caso especial agregada hace que el lenguaje sea más complicado (o, si no se documenta, lo hace menos portátil)

4

Otros ya han señalado que addMsg no es puro y no puede ser puro porque muta el estado del objeto.

La única manera de hacerlo puro es encapsular los cambios que está realizando. La forma más fácil de hacerlo es a través de la mutación de retorno, y hay dos formas de implementar esto.

En primer lugar, usted podría hacerlo de esta manera:

class TestPure 
{ 
    string[] msg; 
    pure TestPure addMsg(string s) 
    { 
     auto r = new TestPure; 
     r.msg = this.msg.dup; 
     r.msg ~= s; 
     return r; 
    } 
} 

Debe copiar la matriz anterior, ya que dentro de una función pura, la referencia this es en realidad const. Tenga en cuenta que puede hacer mejor la copia asignando una nueva matriz del tamaño final y luego copiando los elementos en usted. Se podría utilizar esta función, así:

pure TestPure run3() 
{ 
    auto t = new TestPure; 
    t = t.addMsg("Test"); 
    t = t.addMsg("this."); 
    return t; 
} 

De esta manera, la mutación se limita a cada función puro con cambios pasaron a cabo a través de los valores de retorno.

Una forma alternativa de escribir TestPure sería hacer la const miembros y hacer todas las mutaciones antes de pasarlo al constructor:

class TestPure 
{ 
    const(string[]) msg; 
    this() 
    { 
     msg = null; 
    } 
    this(const(string[]) msg) 
    { 
     this.msg = msg; 
    } 
    pure TestPure addMsg(string s) 
    { 
     return new TestPure(this.msg ~ s); 
    } 
} 

Espero que ayude.

+0

Supongo que esto es probablemente lo mejor que se puede hacer. Solo esperaba poder evitar la generación de toda la basura y el tiempo que pasé haciendo copias de alguna manera. –

+2

Es curioso que diga eso: la mayoría de los lenguajes funcionales generan enormes cantidades de basura por exactamente las mismas razones. Es por eso que sus recolectores de basura tienden a ser tan eficientes. Esto no es algo que se resuelva fácilmente. Si necesita mutar, no use puro. –

+0

Por cierto, esto ya no es cierto. – BCS