Pergunta

Trabalho com TCL e eu gostaria de implementar algo como o Estratégia Padrão . Eu quero passar na "estratégia" para imprimir a saída em uma função TCL, para que eu possa alternar facilmente entre a impressão em tela e impressão para um arquivo de log. Qual é a melhor maneira de fazer isso em TCL?

Foi útil?

Solução

TCL permite que você armazene o nome de um procedimento em uma variável e, em seguida, chamar o procedimento usando essa variável; assim

proc A { x } {
   puts $x
}

set strat A
$strat Hello

irá chamar a proc A e imprimir Olá

Outras dicas

Além da resposta mostrando como você atribuir um procedimento a uma variável, você também pode passar o nome de um procedimento como um argumento para um outro procedimento. Aqui está um exemplo simples:


proc foo { a } {
   puts "a = $a"
}

proc bar { b } {
   puts "b = $b"
}

proc foobar { c } {
   $c 1
}

foobar foo
foobar bar

Isto irá imprimir a = 1 e b = 1

Um exemplo ligeiramente ampliada do que foi listado acima, que pode ilustrar o padrão de estratégia mais claramente:

proc PrintToPDF {document} {
<snip logic>
}

proc PrintToScreen {document} {
<snip logic>
}

proc PrintToPrinter {document} {
<snip logic>
}


set document "my cool formatted document here"

set printMethod "printer"


switch -- $printMethod {
    "printer" {
        set pMethodName "PrintToPrinter"
    }
    "pdf" {
        set pMethodName "PrintToScreen"
    }
    "screen" {
        set pMethodName "PrintToPDF"
    }
}

$pMethodName $document

Além de usar uma proc, você poderia realmente usar um bloco de código em seu lugar. Existem algumas variações sobre este. primeira é a mais óbvia, apenas evaling-lo.

set strategy {
    puts $x
}

set x "Hello"
eval $strategy
unset x

Isso funciona, mas há algumas desvantagens. Primeiro, o óbvio, os dois pedaços de código deve conspirar para usar uma nomenclatura comum para os argumentos. Isto substitui uma dor de cabeça namespace (procs) por outro (moradores), e este é, sem dúvida, na verdade, pior .

Menos óbvio é que eval deliberadamente interpreta seu argumento sem compilar bytecode. Isso ocorre porque presume-se que eval será chamado com gerado dinamicamente, argumentos geralmente únicas, e compilação para bytecode seria ineficiente se o bytecode só seria usado uma vez, em relação a apenas interpretar o bloco imediatamente. Isto é mais fácil de corrigir, então aqui está o idioma:

set x "Hello"
if 1 $strategy
unset x

if, ao contrário eval, faz de compilação e cache do seu bloco de código. Se o bloco $strategy é sempre apenas um ou apenas um punhado de diferentes valores possíveis, então isso funciona muito bem.

Isso não ajuda em nada com o yuckiness de passar argumentos para o bloco com as variáveis ??locais. Há uma série de maneiras de contornar isso, como fazer substituições da mesma forma tk faz substituições no comando argumentos com % de. Você pode tentar fazer algumas coisas hackish utilizando-se uplevel ou upvar. Por exemplo, você poderia fazer isso:

set strategy {
    puts %x
}

if 1 [string map [list %% % %x Hello] $strategy]

Sobre a chance de que os argumentos que estão sendo passados ??não mudam muito, isso funciona bem em termos de compilação bytecode. Se, por outro lado, o argumento muda frequentemente, você deve usar eval vez de if 1. Esta não é muito melhor de qualquer maneira, em termos de argumentos. Há menos probabilidade de confusão sobre o que é passado eo que não é, porque você está usando uma sintaxe especial. Também esta é útil no caso de você querer usar substituição de variável antes de retornar um bloco de código: como em set strategy "$localvar %x".

Felizmente, tcl 8.5 tem verdadeiras funções anônimas , usando o comando apply. A primeira palavra para o comando aplicar seria uma lista dos argumentos e corpo, como se esses argumentos para proc tinha sido retirado. Os argumentos restantes são passados ??para o comando anônimo como argumentos imediatamente.

set strategy [list {x} {
    puts $x
}]

apply $strategy "Hello"

Como sobre o uso de funções variáveis? Eu não me lembro muito TCL (ele tem sido um tempo ...) mas talvez um deles faria o que você precisa:

  • [$ var param1 param2]
  • [$ var] param1 param2
  • $ var param1 param2

Se eu estiver errado, qualquer um é livre para me corrigir.

% set val 4444
4444

% set pointer val
val

% eval puts $$pointer
4444

% puts [ set $pointer ]
4444

% set tmp [ set $pointer ]
4444

Para esclarecer por que método funciona de Jackson, lembre-se que no TCL, tudo é uma string. Se você está trabalhando com uma string literal, uma função, uma variável, ou o que quer que seja, tudo é uma string. Você pode passar um "ponteiro de função" assim como você pode um "ponteiro de dados":. Simplesmente usar o nome do objeto sem líder "$"

Tudo o que disse acima, embora quando se deslocam de namespace ao namespace, você pode querer usar como um [namespace current ]::proc_name passando, para garantir que você não obter qualquer quebra. Compra de métodos OO, você precisa seguir o que é neste segmento: passar por um método de um objecto específico como um argumento de entrada em Tcl
Godspeed.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top