World Playground Deceit.net Blog New Posts 2025 04 Adding keyword parameters to Tcl procs How it's made Movie review: Ford v Ferrari (2019) Music review: Dead Can Dance - Within the Realm of a Dying Sun (1987) Music review: Dark Tranquility - Skydancer (1993) 03 Music review: Darkthrone - Goatlord (1996) Music review: Danzig - Danzig II - Lucifuge (1990) Music review: Crowbar - Crowbar (1993) Speeding up cljqalbum Pruning Portage package config files Music review: Comus - First Utterance (1971) Movie review: Idiocracy (2006) A Common Lisp jq replacement Music review: Cocteau Twins - Heaven or Las Vegas (1990) 02 Music review: Blut Aus Nord - Ultima Thulée (1995) Music questions challenge What are five of your favorite albums? What are five of your favorite songs? Favorite instrument(s)? What song or album are you currently listening to? Do you listen to the radio? If so, how often? How often do you listen to music? How often do you discover music? And how do you discover music? What's a song or album that you enjoy that you wish had more recognition? What's your favorite song of all time? Has your taste in music evolved over the years? Music review: 椎名林檎 (Shiina Ringo) - 勝訴ストリップ (Shōso Strip) (2000) Linux pipe(2) vs ring buffer Novel review: Robert Heinlein - The Moon is a Harsh Mistress (1966) Music review: Black Flag - My War (1984) 01 Music review: Belkètre - Ambre Zuèrkl Vuorhdrévarvtre (1996) Cooking recipe: bœuf bourguignon Most memorable extreme music screaming Music review: Amesoeurs - Ruines Humaines (2006) Novel review: Terry Pratchett - Mort (1987) Novel review: Mark Danielewski - House of Leaves (2000) PDF to CBZ for e-reader Music review: Acid Bath - Paegan Terrorism Tactics (1996) Video game review: Bloodstained: Ritual of the Night (2019) Dotfiles management 2024 12 Novel Review: Liu Cixin - The Three-Body Problem (2008) GNU parallel out, pararun in Advent of Code 2024: retrospective Advent of Code 2024: Day 01 11 How I Learned to Stop Worrying and Love GC Initial C weenie stance Insights and updated views New stance See also Music review: Thee Maldoror Kollective - New Era Viral Order (Dogma Slaughterhouse and the Children of Anaemia) (2002) Novel review: Terry Pratchett - Guards! Guards! (1989) & Men at Arms (1993) Smuggling files under Google's nose First blood: nested zips Second skirmish: file signature doctoring Final setback and victory My archives Emacs: separate minibuffer history for a command Music review: Techno Animal - The Brotherhood of the Bomb (2001) CL iterate's COLLECT performance notice 10 Music review: Burzum - Det som engang var (1993) QoL addition to Common Lisp :start/:end Closures in Tcl What kind of closures In Tcl Music review: Abigor - Supreme Immortal Art (1998) git secret filter My sfeed setup Introduction Sfeed praise Personal additions Why I like Tcl Pros Cons Conclusion 09 Music review: Samael - Eternal (1999) Movie review: The Crow (1994) Bourne shell stdio trivia while read loops and interactive commands Default stream buffering Tags fantasy (1) advent of code (2) black metal (5) blackgaze (1) comedy (3) computer science (1) cooking recipe (1) darkwave (1) dotfiles (5) emacs (1) fantasy (1) folk (1) gentoo (1) git (1) gothic metal (1) hacking (1) heavy metal (1) hip hop (1) horror (1) image processing (1) industrial metal (1) j-rock (1) lisp (4) melodic death metal (1) movie review (3) music review (19) music (1) novel review (5) performance (3) personal (2) post-punk (1) programming (14) punk (1) science fiction (2) sh (4) sludge metal (1) stoner metal (1) tcl (3) video game review (1) About Me Website Raison d'être Design choices Links Webrings Lainring Code

Adding keyword parameters to Tcl procs

Published on 2025-04-21 Tags: tcl, programming

编程语言中,有两件事让我特别恼火:一是缺少关键字(可选、始终命名且与顺序无关)参数,二是语言内置功能对用户不可用。

Tcl 在这方面表现得很糟糕,因为它的许多标准命令都有类似 UNIX 的选项,但这些在 proc () 或 apply () 中都无法使用。

但由于 proc 可以“轻松地”被包装(稍后详细说明),我花了几个小时实现了这个:

source .../util.tcl
namespace path {::util ::tcl::mathop ::tcl::mathfunc}
proc* p {-flag {-opt foo} x args} {
  puts [lmap v {flag opt x args} {string cat "$v=[set $v]"}]
}
p 1 a;        # => flag=0 opt=foo x=1 args=a
p -opt bar -flag 1 a; # => flag=1 opt=bar x=1 args=a
p -flag;       # => flag=0 opt=foo x=-flag args=

# Relatively constrained overhead
proc p1 {x} {}
proc* p2 {-flag {-opt foo} x} {}
timerate {p1 1}; # => 0.127049 µs/# 7870986 # 7870986 #/sec 1000.000 net-ms
timerate {p2 1}; # => 0.794317 µs/# 1258943 # 1258943 #/sec 1000.000 net-ms

还不错吧?正如你在第三个例子中看到的,我甚至实现了 Tcl 的行为,即强制性的位置参数数量用于消除它们与选项的歧义(这就是你如何执行 puts $string 而不必使用 -- 标记保护它的方式)。

实现方式 §

关于前面提到的“轻松地”,我想补充说明一下它的实现过程;绝对 不适合胆小的人。

proc proc* {name args body} {
  if {[llength $args] == 0 || ![string match "-*" [lindex $args 0]]} {
    proc $name $args $body
    return
  }
  while {[llength $args] && [string match "-*" [lindex $args 0]]} {
    set args [lassign $args[set args {}] arg]
    switch [llength $arg] {
      1 { # Flag
        lappend optargs [set var [string range $arg 1 end]]
        lappend optinit 0
        lappend optswitch $arg [list set $var 1]
      }
      2 { # Option
        lassign $arg opt init
        lappend optargs [set var [string range $opt 1 end]]
        lappend optinit $init
        lappend optswitch $opt "set [list $var] \[lindex \$args \[incr i]]"
      }
      default {error "$arg: unknown option format"}
    }
  }
  lappend optswitch default {error "$arg: unknown option"}
  # Count mandatory positional args to disambiguate with options
  set mcount 0
  foreach arg $args {
    if {[llength $arg] != 1 || $arg eq "args"} break
    incr mcount
  }
  tailcall proc $name args [quasiquote {
    lassign `$optinit `@$optargs
    for {set i 0; set end [expr {[llength $args] - `$mcount}]} {$i < $end} {incr i} {
      set arg [lindex $args $i]
      if {![string match "-*" $arg] || $arg eq "--"} break
      switch -- $arg `$optswitch
    }
    apply `[list [list {*}$optargs {*}$args] $body]` \
      `@[join [lmap o $optargs {string cat \$$o}]]` {*}[lrange $args $i end]
  }]
}

你可能会说,还不错。我同意,这里只是一些非常基本的预处理和代码生成。不,问题在于用于进行最终生成的那个不起眼的 quasiquote 命令。

你看,Tcl 的元编程能力很差,因为它缺乏选择性地在字符串中 subst () 的方法,所以每个人都在做什么呢?自己实现基于字符串的模板系统!事实上,我认为一个 hacker 如果不制作类似的东西,就无法使用 Tcl 一段时间(参见 wiki)。

在我的例子中,这是令人厌恶但有效的尝试(忽略 ?move 辅助函数):

# CL-like quasiquoting, with `$ and `[...]` for unquoting and `@$ and `@[...]` for splicing
# NB: ` chosen for its lack of standard meaning in T