Tcl: идиоматический [закрытие] - PullRequest
0 голосов
/ 21 мая 2018

Tcl имеет применяется и лямбда , но не closure.

По состоянию на tcl 8,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}

Ответы [ 2 ]

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

Tcl не делает полных замыканий, но может делать их ограниченные версии для ключевых случаев использования;если вы видите {*}, примененное к очевидному первому слову команды, то это именно то, что происходит.Например, вы использовали (объект) callback сценарий использования.Это довольно просто сделать:

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 не собирает неиспользуемые команды.

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

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

Это «замыкание» не имеет изменяемого состояния, но это можно исправить.Это (один из способов сделать это) «генератор аккумуляторов» Пола Грэма :

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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...