Tcl:惯用[关闭]

时间:2018-05-21 16:53:44

标签: closures tcl

Tcl有applylambda,但没有tcl

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

2 个答案:

答案 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没有垃圾收集未使用的命令。