为 Tcl Procs 添加关键字参数
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