Preguiça de Avaliação De Argumentos de Função

Relatar um bug

Se você encontrar um problema com esta página, clique aqui para criar um Bugzilla problema.

melhorar esta página

rapidamente bifurcar, Editar on-line, e enviar um pull request para esta página.Requer uma conta do GitHub com login. Isso funciona bem para pequenas mudanças.Se você quiser fazer alterações maiores, considere usarum clone local.

por Walter Bright, http://www.digitalmars.com/d

avaliação preguiçosa é a técnica de não avaliar uma expressão sem expressão e até que o resultado da expressão seja necessário.O & &, / / e ?: os operadores são a maneira convencional de avaliar preguiçosamente:

void test(int* p){ if (p && p) ...}

a segunda expressão p não é avaliada a menos que pis não seja nulo.Se a segunda expressão não fosse avaliada preguiçosamente, ela geraria uma falha de tempo de execução se p fosse nulo.

embora inestimável, os operadores de avaliação preguiçosos têm significativalimitações. Considere uma função de registro, que logsa mensagem, e pode ser ativado e desativado em tempo de execução baseado em um globalvalue:

void log(const(char) message){ if (logging) fwritefln(logfile, message);}

muitas Vezes, a seqüência de mensagem irá ser construído em tempo de execução:

void foo(int i){ log("Entering foo() with i set to " ~ toString(i));}

Enquanto isso funciona, o problema é que a construção do messagestring acontece independentemente de se o log está habilitado ou não.Com aplicativos que fazem uso pesado de registro, isso pode se tornarum dreno terrível no desempenho.

uma maneira de corrigi-lo é usando avaliação preguiçosa:

void foo(int i){ if (logging) log("Entering foo() with i set to " ~ toString(i));}

mas isso viola os princípios de encapsulamento, expondo os detalhes do registro ao usuário. Em C, Esse problema é frequentemente trabalhadoao usar uma macro:

#define LOG(string) (logging && log(string))

mas isso apenas papéis sobre o problema. Macros pré-processador têmbem conhecidas deficiências:

  • a variável de registro é exposta no namespace do Usuário.As Macros são invisíveis para depuradores simbólicos.
  • as Macros são globais e não podem ter escopo.
  • as Macros não podem ser membros da classe.
  • as Macros não podem ter seu endereço usado, portanto, não podem ser passadas indiretamente como as funções podem.

uma solução robusta seria uma maneira de fazer uma avaliação preguiçosa dos parâmetros da função. Tal maneiraé possível na linguagem de programação D usando um parâmetro delegado:

void log(const(char) delegate() dg){ if (logging) fwritefln(logfile, dg());}void foo(int i){ log( { return "Entering foo() with i set to " ~ toString(i); });}

agora, a expressão de construção de string só é avaliada se loggingis for verdadeiro e o encapsulamento for mantido. O único problema é quefew vai querer envolver expressões com { return exp;}.

então D leva um pequeno, mas crucial, passo adiante (sugerido por Andrei Alexandrescu).Qualquer expressãopode ser implicitamente convertido em um delegado que retorna void ouo tipo da expressão.A declaração de delegado é substituída pela classe lazy storage(sugerida por Tomasz Stachowiak).As funções então se tornam:

void log(lazy const(char) dg){ if (logging) fwritefln(logfile, dg());}void foo(int i){ log("Entering foo() with i set to " ~ toString(i));}

Qual é a nossa versão original, exceto que agora a string não éconstruída, a menos que o registro esteja ativado.

sempre que houver um padrão repetido visto no Código, ser capaz de eliminar esse padrão e encapsulá-lo significa que podemos reduzir a complexidade do código e, portanto, os bugs. O exemplo mais comum deEsta é a funçãovocê mesmo.A avaliação preguiçosa permite o encapsulamento de uma série de outros padrões.

para um exemplo simples, suponha que uma expressão seja avaliada counttimes. O padrão é:

for (int i = 0; i < count; i++) exp;

Este padrão pode ser encapsulada em uma função usando avaliação preguiçosa:

void dotimes(int count, lazy void exp){ for (int i = 0; i < count; i++) exp();}

Ele pode ser usado como:

void foo(){ int x = 0; dotimes(10, write(x++));}

o que irá imprimir:

0123456789

Mais complexo definidos pelo usuário estruturas de controle são possíveis.Aqui está um método para criar uma estrutura semelhante a um switch:

bool scase(bool b, lazy void dg){ if (b) dg(); return b;}/* Here the variadic arguments are converted to delegates in this special case. */void cond(bool delegate() cases ...){ foreach (c; cases) { if (c()) break; }}

que pode ser usado como:

void foo(){ int v = 2; cond ( scase(v == 1, writeln("it is 1")), scase(v == 2, writeln("it is 2")), scase(v == 3, writeln("it is 3")), scase(true, writeln("it is the default")) );}

que irá imprimir:

it is 2

aqueles familiarizados com a linguagem de programação Lisp notarão algunsintrigindo paralelos com macros Lisp.

para um último exemplo, existe o padrão comum:

Abc p;p = foo();if (!p) throw new Exception("foo() failed");p.bar(); // now use p

como throw é uma instrução, não uma expressão,expressões queprecisa fazer isso precisa ser dividido em várias instruções e variáveis extras são introduzidas.(Para um tratamento completo desta questão, ver Andrei Alexandrescu epetru Marginean’s paperEnforcements).Com a avaliação preguiçosa, tudo isso pode ser encapsulado em uma função única:

Abc Enforce(Abc p, lazy const(char) msg){ if (!p) throw new Exception(msg()); return p;}

e o exemplo de abertura acima torna-se simplesmente:

Enforce(foo(), "foo() failed").bar();

e 5 linhas de código se tornam uma. Impor pode ser melhorado tornando-o função atemplate:

T Enforce(T)(T p, lazy const(char) msg){ if (!p) throw new Exception(msg()); return p;}

conclusão

a avaliação preguiçosa de argumentos de função amplia drasticamente a expressividade das funções. Ele permite o encapsulamento em funções de muitospadrões de codificação comuns e expressões idiomáticas que antes eram muito desajeitadas ou práticas para fazer.

agradecimentos

agradeço a inspiração e a assistência de Andrei Alexandrescu, Bartosz Milewski e David Held. A comunidade D ajudou muito com muitas críticas construtivas, como o tópico começando com Tomasz Stachowiak Em D/41633.

Deixe uma resposta

O seu endereço de email não será publicado.