从closure
8.6开始,什么是惯用形式或#!/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
?
已发布的patterns似乎令人困惑,下面也是如此。
示例:
::Main::performPath {one two omega gamma}
输出:
while True:
word = input("Enter a word ('quit' to quit): ")
ignoreCase = "quit"
vowels = "aeiou"
if word.lower() == ignoreCase.lower():
break
elif word == "":
print("Can't convert empty string. Try again.")
for index,c in enumerate(word.lower()):
if c in vowels:
print(word.lower()[0:] + "way".lower())
break
elif vowels: #No vowel statement
while word.lower()[0] not in vowels:
word = word.lower()[1:] + word.lower()[0]
print(word.lower() + "ay".lower())
break
答案 0 :(得分:3)
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的"accumulator generator":
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
}
答案 1 :(得分:2)
Tcl没有完全关闭,但它可以为关键用例做有限的版本;如果你看到{*}
应用于命令的明显第一个单词,那就是那种正在发生的事情。例如,您正在执行(对象)回调用例。这很简单:
set performPath [namespace code [list my performPath $url {*}$args]]
(namespace code
确保将在正确的命名空间中评估回调,即使从对象外部运行也是如此。)
我们甚至可以通过定义帮助程序来制作那个整理器:
proc ::oo::Helpers::callback {method args} {
tailcall namespace code [list my $method {*}$args]
}
set performPath [callback performPath $url {*}$args]
同样,变量捕获用例也可以完成。这是最简单的版本,假设所有变量都不是数组:
proc closure {body} {
set binding {}
foreach v [uplevel 1 info locals] {
upvar 1 $v var
if {[info exists var]} {
lappend binding [list $v $var]
}
}
return [list apply [list $binding $body [uplevel 1 namespace current]]]
}
演示如何使用它:
proc foo {n} {
set result {}
for {set i 1} {$i <= $n} {incr i} {
lappend result [closure {
puts "This is $i of $n"
}]
}
return $result
}
foreach c [lreverse [foo 10]] {
{*}$c
}
(处理数组和参数会使这更复杂。)
如果你需要在“闭包”中修改状态,那么你需要使用一个对象或一个协程来保持状态。其中任何一个的主要问题是你需要在完成后明确清理生成的命令;标准Tcl没有垃圾收集未使用的命令。