首页 > 解决方案 > Tcl:惯用的[关闭]

问题描述

Tcl 有applylambda,但没有closure

tcl8.6 开始,什么是惯用形式或closure

已发布的模式看起来令人困惑,如下所示。

例子:

#!/usr/bin/env tclsh
::oo::class create Main {
    method ensurePath {url args} {
        # closure definition, takes time to recognize
        set performPath [list my performPath $url {*}$args] 
        if {0} {
            # closure application, can reduce needless noise?
            {*}$performPath alpha beta
        } elseif {1} {
            {*}$performPath omega gamma
        } else {
            # no performPath
        }
    }
    method performPath {url args} {
        puts "[self class]::[self method] {$args}"
    }
}
set main [Main new]
$main ensurePath url one two

输出:

::Main::performPath {one two omega gamma}

标签: closurestcl

解决方案


AFAIK,没有惯用的方法来指定 Tcl 中的闭包。虽然闭包所做的事情可以在 Tcl 中模拟,但核心概念与 Tcl 是正交的。

经典的加法器闭包示例(来自Wikipedia

function add(x)
   function addX(y)
       return y + x
   return addX

variable add1 = add(1)
variable add5 = add(5)

assert add1(3) = 4
assert add5(3) = 8

可以用 Tcl 写成

proc add {name x} {
    interp alias {} $name {} ::tcl::mathop::+ $x
}

add add1 1
add add5 5

add1 3
# => 4
add5 3
# => 8

这个“闭包”没有可变状态,但可以修复。这是(一种方法*)Paul Graham 的“累加器生成器”

proc foo {name n} {
    set int [interp create]
    $int eval set n $n
    $int eval {proc _foo i {incr ::n $i}}
    interp alias {} $name $int _foo
}

但是那个不允许共享状态......等等。

我认为在 Tcl 中处理闭包的最佳方法是确定闭包为什么有用,并查看是否没有 Tcl 习惯用法可以做到这一点。像这样的闭包并不是真正的 Tcl 事情。


*) 这是另一个,我不知道是否应该考虑这个更好。

proc _foo i {
    set n $i
    while 1 {
        incr n [yield $n]
    }
}

proc foo {name n} {
    coroutine $name _foo $n
}

推荐阅读