Como utilizar puro em D 2.0
-
06-07-2019 - |
Pergunta
Enquanto a brincar com D 2.0 Eu encontrei o seguinte problema:
Exemplo 1:
pure string[] run1()
{
string[] msg;
msg ~= "Test";
msg ~= "this.";
return msg;
}
Isso compila e funciona como esperado.
Quando tento enrole a matriz de cadeia em uma classe eu acho que eu não posso chegar a este trabalho:
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 não irá compilar porque a função addMsg é impuro. Eu não posso fazer essa função pura, uma vez que altera o objeto TestPure. Estou esquecendo de algo? Ou esta é uma limitação?
A seguir faz compilação:
pure TestPure run3()
{
TestPure t = new TestPure();
t.msg ~= "Test";
t.msg ~= "this.";
return t;
}
O relator ~ = operador não foi implementado como uma função impuro da matriz msg? Como é que o compilador não reclamar sobre isso na função RUN1?
Solução
Desde v2.050, D relaxou a definição de pure
para aceitar as chamadas funções "fracamente puros" também. Isto refere-se às funções que " não ler ou escrever qualquer mutável mundial estado ". Fracamente funções puras são não as mesmas funciona como puros no sentido linguagem funcional. A única relação é que torna as funções reais puros, também chamadas funções "fortemente puros" chamada capazes os fracos, como o exemplo do OP.
Com isso, addMsg
pode ser marcado como (fracamente) pure
, uma vez que apenas o this.msg
variável local é alterado:
class TestPure
{
string[] msg;
pure void addMsg( string s )
{
msg ~= s;
}
};
e, claro, agora você pode usar o (fortemente) pure
função run2
sem modificação.
pure TestPure run2()
{
TestPure t = new TestPure();
t.addMsg("Test");
t.addMsg("this.");
return t;
}
Outras dicas
Outros já têm apontado que addMsg não é puro e não pode ser puro, pois transforma o estado do objeto.
A única maneira de torná-lo puro é encapsular o que você está fazendo mudanças. A maneira mais fácil de fazer isso é através de mutação retorno, e há duas maneiras de implementar isso.
Em primeiro lugar, você poderia fazê-lo como este:
class TestPure
{
string[] msg;
pure TestPure addMsg(string s)
{
auto r = new TestPure;
r.msg = this.msg.dup;
r.msg ~= s;
return r;
}
}
Você precisa copiar a matriz anterior, porque dentro de uma função pura, a esta referência é realmente const. Note que você poderia fazer a cópia melhor através da atribuição de uma nova matriz do tamanho final e, em seguida, copiar os elementos em si mesmo. Você usaria essa função assim:
pure TestPure run3()
{
auto t = new TestPure;
t = t.addMsg("Test");
t = t.addMsg("this.");
return t;
}
Desta forma, a mutação está confinado a cada função pura com as mudanças aprovadas para fora através de valores de retorno.
Uma forma alternativa de escrever TestPure seria fazer os membros const e fazer tudo a mutação antes de passá-lo para o construtor:
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 ajude.
Por favor, reveja a definição de funções puras:
- http://en.wikipedia.org/wiki/Pure_function
- http://www.digitalmars.com/d/2.0 /function.html#pure-functions
funções puras são funções que produzem o mesmo resultado para os mesmos argumentos. Para o efeito, a função pura:
- tem parâmetros que são todos invariante ou são implicitamente conversível para invariante
- não ler ou escrever qualquer estado mutável mundial
Um dos efeitos do uso de funções puras é que eles podem ser paralelizado com segurança. No entanto, não é seguro para executar várias instâncias de sua função em paralelo, porque eles poderiam tanto modificar a instância da classe simultaneamente, causando um problema de sincronização.
I pensar que o seu código é conceitualmente correto. No entanto, você pode ter encontrado caso em análise semântica do compilador não é tão bom quanto o seu cérebro de.
Considere o caso em que fonte da classe não está disponível. Em que casos o compilador teria nenhuma maneira de dizer que a variável addMsg
apenas modifica membro para que ele não pode permitir que você chamá-lo de uma função pura.
Para permitir que no seu caso, ele teria que ter tratamento especial caso para este tipo de uso. Toda regra caso especial adicionado torna a linguagem mais complicado (ou, se deixados em situação irregular, torna menos portátil)
Apenas um palpite, mas esta função não retornar sempre o mesmo resultado.
Veja, ele retorna uma referência a algum objeto, e enquanto o objeto sempre conterá os mesmos dados, os objetos retornados por várias chamadas para as mesmas funções não são idênticas; isto é, eles não têm o mesmo endereço de memória.
Quando você retornar uma referência para o objeto, você está retornando essencialmente um endereço de memória, que vai ser diferente em várias chamadas.
Outra maneira de pensar sobre isso, parte do valor de retorno é o endereço de memória de um objeto, que é dependente de algum estado global (s), e se a saída de uma função depende do estado global, então não é puro . Inferno, ele mesmo não tem que depender dela; enquanto uma função lê um estado global, então não é puro. Ao chamar "novo", você está lendo estado global.