Есть ли в TCL понятие указателей на функции? - PullRequest
3 голосов
/ 31 октября 2008

Работая с TCL, я бы хотел реализовать что-то вроде Pattern Pattern . Я хочу передать «стратегию» печати на выходе в функции TCL, чтобы я мог легко переключаться между печатью на экран и печатью в файл журнала. Какой лучший способ сделать это в TCL?

Ответы [ 8 ]

18 голосов
/ 31 октября 2008

TCL позволяет сохранить имя процедуры в переменной, а затем вызвать процедуру, используя эту переменную; так

proc A { x } {
   puts $x
}

set strat A
$strat Hello

вызовет proc A и распечатает Hello

5 голосов
/ 08 ноября 2008

В дополнение к ответу, показывающему, как вы назначаете процедуру переменной, вы также можете передать имя процедуры в качестве аргумента другой процедуре. Вот простой пример:


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

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

proc foobar { c } {
   $c 1
}

foobar foo
foobar bar

Это напечатает a = 1 и b = 1

4 голосов
/ 01 ноября 2008

Немного расширенный пример того, что было перечислено выше, которое могло бы более наглядно проиллюстрировать Шаблон стратегии:

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
3 голосов
/ 25 июля 2009

Помимо использования proc, вы можете вместо этого использовать блок кода. Есть несколько вариантов этого. первый - самый очевидный, просто eval его.

set strategy {
    puts $x
}

set x "Hello"
eval $strategy
unset x

Это работает, но есть несколько минусов. Во-первых, очевидно, что обе части кода должны сговориться с использованием общего именования аргументов. Это заменяет одну головную боль в пространстве имен (procs) другой (localals), и это, вероятно, на самом деле хуже .

Менее очевидно, что eval намеренно интерпретирует свой аргумент без компиляции байт-кода. Это потому, что предполагается, что eval будет вызываться с динамически генерируемыми, обычно уникальными аргументами, и компиляция в байт-код будет неэффективной, если байт-код будет использоваться только один раз, по сравнению с простой интерпретацией блока немедленно. Это легче исправить, поэтому вот идиома:

set x "Hello"
if 1 $strategy
unset x

if, в отличие от eval, компилирует и кэширует свой блок кода. Если в блоке $strategy только одно или несколько различных возможных значений, то это работает очень хорошо.

Это совсем не помогает в поспешности передачи аргументов в блок с локальными переменными. Существует множество способов, например, подстановок таким же образом, как tk выполняет подстановку аргументов команды с %. Вы можете попробовать сделать некоторые хакерские вещи, используя uplevel или upvar. Например, вы можете сделать это:

set strategy {
    puts %x
}

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

Если вероятность того, что передаваемые аргументы не сильно изменятся, это хорошо с точки зрения компиляции байт-кода. С другой стороны, если аргумент часто меняется, вы должны использовать eval вместо if 1. В любом случае, это не намного лучше с точки зрения аргументов. Вероятность путаницы в отношении того, что передано, а что нет, меньше, потому что вы используете специальный синтаксис. Также это полезно, если вы хотите использовать подстановку переменных перед возвратом блока кода: как в set strategy "$localvar %x".

К счастью, tcl 8.5 имеет истинных анонимных функций , использующих команду apply. Первым словом в команде apply будет список аргументов и тела, как если бы эти аргументы для proc были исключены. Остальные аргументы передаются анонимной команде в качестве аргументов немедленно.

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

apply $strategy "Hello"
1 голос
/ 18 августа 2009
% set val 4444
4444

% set pointer val
val

% eval puts $$pointer
4444

% puts [ set $pointer ]
4444

% set tmp [ set $pointer ]
4444
1 голос
/ 31 октября 2008

Как насчет использования переменных функций? Я не помню много TCL (это было давно ...), но, возможно, один из них сделает то, что вам нужно:

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

Если я ошибаюсь, любой может меня поправить.

0 голосов
/ 31 мая 2018

Все вышеперечисленное, хотя при переходе от пространства имен к пространству имен вы можете использовать в качестве пропуска [namespace current ]::proc_name, чтобы гарантировать отсутствие разрывов.
Для методов OO вам понадобятся чтобы следовать тому, что находится в этой теме: Передать метод определенного объекта в качестве входного аргумента в Tcl
Godspeed.

0 голосов
/ 18 марта 2009

Чтобы уточнить, почему работает метод Джексона, помните, что в TCL all является строкой. Работаете ли вы с литеральной строкой, функцией, переменной или чем-то еще, все является строкой. Вы можете передать «указатель на функцию» так же, как «указатель на данные»: просто используйте имя объекта без начального «$».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...