【问题标题】:Does TCL have some concept of function pointers?TCL有函数指针的概念吗?
【发布时间】:2018-05-31 19:06:22
【问题描述】:

与 TCL 合作,我想实现类似 Strategy Pattern 的东西。我想在 TCL 函数中传递打印输出的“策略”,这样我就可以轻松地在打印到屏幕和打印到日志文件之间切换。在 TCL 中执行此操作的最佳方法是什么?

【问题讨论】:

  • 如果您对实现策略模式有疑问,请不要在 TCL 中询问函数指针。

标签: design-patterns function-pointers tcl


【解决方案1】:

TCL 允许您将过程的名称存储在一个变量中,然后使用该变量调用该过程;所以

proc A { x } {
   puts $x
}

set strat A
$strat Hello

将调用proc A并打印出Hello

【讨论】:

    【解决方案2】:

    除了显示如何将过程分配给变量的答案之外,您还可以将过程的名称作为参数传递给另一个过程。这是一个简单的例子:

    
    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

    【讨论】:

      【解决方案3】:

      上面列出的稍微扩展的示例可以更清楚地说明策略模式:

      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
      

      【讨论】:

        【解决方案4】:

        除了使用 proc,您实际上还可以使用代码块。这有一些变化。首先是最明显的,只需 evaling 即可。

        set strategy {
            puts $x
        }
        
        set x "Hello"
        eval $strategy
        unset x
        

        这可行,但也有一些缺点。首先很明显,两段代码必须共同使用参数的通用命名。这用另一种(本地)替换了一个令人头疼的命名空间(procs),这实际上可以说是更糟糕

        不太明显的是 eval 故意解释它的参数而不编译字节码。这是因为假设 eval 将使用动态生成的、通常是唯一的参数调用,并且如果字节码只使用一次,相对于立即解释块,编译为字节码的效率会很低。这更容易解决,所以这是成语:

        set x "Hello"
        if 1 $strategy
        unset x
        

        ifeval 不同,它会编译和缓存它的代码块。如果$strategy 块只是一个或几个不同的可能值,那么这非常有效。

        这对于将参数传递给带有局部变量的块的讨厌没有任何帮助。有很多方法可以解决这个问题,例如执行substitutions,就像tk 用% 替换命令参数一样。您可以尝试使用 uplevelupvar 做一些骇人听闻的事情。例如,您可以这样做:

        set strategy {
            puts %x
        }
        
        if 1 [string map [list %% % %x Hello] $strategy]
        

        如果传递的参数变化不大,这在字节码编译方面效果很好。另一方面,如果参数经常更改,则应使用eval 而不是if 1。就论点而言,这无论如何也好不了多少。由于您使用的是特殊语法,因此不太可能混淆已通过和未通过的内容。如果您想在返回代码块之前使用变量替换,这也很有帮助:如set strategy "$localvar %x"

        幸运的是,tcl 8.5 有true anonymous functions,使用apply 命令。 apply 命令的第一个词是参数和正文的列表,就好像proc 的那些参数已被取出。其余参数立即作为参数传递给匿名命令。

        set strategy [list {x} {
            puts $x
        }]
        
        apply $strategy "Hello"
        

        【讨论】:

        • 值得指出的是,您可以在 tcl8.0(或更高版本)中使用一个 proc 来获得同样的行为,该 proc 在应用手册页上出于说明目的而显示。如果您使用的东西更早,那么,我觉得是的。
        【解决方案5】:
        % set val 4444
        4444
        
        % set pointer val
        val
        
        % eval puts $$pointer
        4444
        
        % puts [ set $pointer ]
        4444
        
        % set tmp [ set $pointer ]
        4444
        

        【讨论】:

          【解决方案6】:

          如何使用变量函数?我不记得太多 TCL(已经有一段时间了......)但也许其中一个可以满足您的需要:

          • [$var param1 param2]
          • [$var] 参数 1 参数 2
          • $var 参数1 参数2

          如果我错了,任何人都可以纠正我。

          【讨论】:

          • 这是您应该使用的最后一个。
          【解决方案7】:

          要阐明 Jackson 的方法为何有效,请记住在 TCL 中,everything 是一个字符串。无论您使用的是文字字符串、函数、变量还是其他任何东西,everything 都是字符串。您可以像传递“数据指针”一样传递“函数指针”:只需使用不带前导“$”的对象名称即可。

          【讨论】:

            【解决方案8】:

            上述所有内容,尽管在从命名空间移动到命名空间时,您可能希望将[namespace current ]::proc_name 用作传递的[namespace current ]::proc_name,以确保不会出现任何中断。
            对于 OO 方法,您需要关注此线程中的内容:Pass a method of a specific object as an input argument in Tcl
            神速。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-03-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多