Problem creating new command in `array` ensemble
I want to create a convenience command array values arrayName
as a "flip side" to the "array names" command.
It's straightforward to create a simple proc:
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
array set a {foo bar baz qux}
puts [array_values a] ;# => bar qux
However, I'm having difficulty creating a command in the ::tcl::array
namespace:
first some homework:
is
array
a namespace ensemble? Yes.
% namespace ensemble exists array
1
what is the namespace?
% namespace ensemble configure array -namespace
::tcl::array
what are the subcommands?
% namespace ensemble configure array -subcommands
% namespace ensemble configure array -map
anymore ::tcl::array::anymore donesearch ::tcl::array::donesearch exists ::tcl::array::exists get ::tcl::array::get names ::tcl::array::names nextelement ::tcl::array::nextelement set ::tcl::array::set size ::tcl::array::size startsearch ::tcl::array::startsearch statistics ::tcl::array::statistics unset ::tcl::array::unset
OK, all good so var. Let's add that array_values
proc into the namespace
% namespace eval ::tcl::array {
proc values {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
}
% array set a {foo bar baz qux}
% puts [::tcl::array::values a]
can't set "values": variable is array
Where is this error coming from? I tried renaming the "values" variable in the proc to other names, but it still emits the "variable is array" error.
a note: I can add the first proc to the ensemble:
% namespace ensemble config array -map [list values ::array_values {*}[namespace ensemble config array -map]]
% array values a
bar qux
But what is wrong with my ::tcl::array::values
proc?
tcl
add a comment |
I want to create a convenience command array values arrayName
as a "flip side" to the "array names" command.
It's straightforward to create a simple proc:
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
array set a {foo bar baz qux}
puts [array_values a] ;# => bar qux
However, I'm having difficulty creating a command in the ::tcl::array
namespace:
first some homework:
is
array
a namespace ensemble? Yes.
% namespace ensemble exists array
1
what is the namespace?
% namespace ensemble configure array -namespace
::tcl::array
what are the subcommands?
% namespace ensemble configure array -subcommands
% namespace ensemble configure array -map
anymore ::tcl::array::anymore donesearch ::tcl::array::donesearch exists ::tcl::array::exists get ::tcl::array::get names ::tcl::array::names nextelement ::tcl::array::nextelement set ::tcl::array::set size ::tcl::array::size startsearch ::tcl::array::startsearch statistics ::tcl::array::statistics unset ::tcl::array::unset
OK, all good so var. Let's add that array_values
proc into the namespace
% namespace eval ::tcl::array {
proc values {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
}
% array set a {foo bar baz qux}
% puts [::tcl::array::values a]
can't set "values": variable is array
Where is this error coming from? I tried renaming the "values" variable in the proc to other names, but it still emits the "variable is array" error.
a note: I can add the first proc to the ensemble:
% namespace ensemble config array -map [list values ::array_values {*}[namespace ensemble config array -map]]
% array values a
bar qux
But what is wrong with my ::tcl::array::values
proc?
tcl
As a follow-up on Schelte's answer: Alsoforeach
would have to be qualified, since ::tcl::array::foreach is about to become available (in 8.7, if I am right)? See also my complementary answer.
– mrcalvin
Nov 19 '18 at 21:03
Just to close this loop, TP 421 implementsarray for
-- core.tcl.tk/tcl/info/7c614b93309da90a
– glenn jackman
Nov 20 '18 at 13:54
Correct, I was confused by the progenitor discussion on the corresponding bounty: github.com/flightaware/Tcl-bounties/issues/27
– mrcalvin
Nov 20 '18 at 14:14
add a comment |
I want to create a convenience command array values arrayName
as a "flip side" to the "array names" command.
It's straightforward to create a simple proc:
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
array set a {foo bar baz qux}
puts [array_values a] ;# => bar qux
However, I'm having difficulty creating a command in the ::tcl::array
namespace:
first some homework:
is
array
a namespace ensemble? Yes.
% namespace ensemble exists array
1
what is the namespace?
% namespace ensemble configure array -namespace
::tcl::array
what are the subcommands?
% namespace ensemble configure array -subcommands
% namespace ensemble configure array -map
anymore ::tcl::array::anymore donesearch ::tcl::array::donesearch exists ::tcl::array::exists get ::tcl::array::get names ::tcl::array::names nextelement ::tcl::array::nextelement set ::tcl::array::set size ::tcl::array::size startsearch ::tcl::array::startsearch statistics ::tcl::array::statistics unset ::tcl::array::unset
OK, all good so var. Let's add that array_values
proc into the namespace
% namespace eval ::tcl::array {
proc values {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
}
% array set a {foo bar baz qux}
% puts [::tcl::array::values a]
can't set "values": variable is array
Where is this error coming from? I tried renaming the "values" variable in the proc to other names, but it still emits the "variable is array" error.
a note: I can add the first proc to the ensemble:
% namespace ensemble config array -map [list values ::array_values {*}[namespace ensemble config array -map]]
% array values a
bar qux
But what is wrong with my ::tcl::array::values
proc?
tcl
I want to create a convenience command array values arrayName
as a "flip side" to the "array names" command.
It's straightforward to create a simple proc:
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
array set a {foo bar baz qux}
puts [array_values a] ;# => bar qux
However, I'm having difficulty creating a command in the ::tcl::array
namespace:
first some homework:
is
array
a namespace ensemble? Yes.
% namespace ensemble exists array
1
what is the namespace?
% namespace ensemble configure array -namespace
::tcl::array
what are the subcommands?
% namespace ensemble configure array -subcommands
% namespace ensemble configure array -map
anymore ::tcl::array::anymore donesearch ::tcl::array::donesearch exists ::tcl::array::exists get ::tcl::array::get names ::tcl::array::names nextelement ::tcl::array::nextelement set ::tcl::array::set size ::tcl::array::size startsearch ::tcl::array::startsearch statistics ::tcl::array::statistics unset ::tcl::array::unset
OK, all good so var. Let's add that array_values
proc into the namespace
% namespace eval ::tcl::array {
proc values {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
}
% array set a {foo bar baz qux}
% puts [::tcl::array::values a]
can't set "values": variable is array
Where is this error coming from? I tried renaming the "values" variable in the proc to other names, but it still emits the "variable is array" error.
a note: I can add the first proc to the ensemble:
% namespace ensemble config array -map [list values ::array_values {*}[namespace ensemble config array -map]]
% array values a
bar qux
But what is wrong with my ::tcl::array::values
proc?
tcl
tcl
asked Nov 19 '18 at 17:40
glenn jackman
166k26143234
166k26143234
As a follow-up on Schelte's answer: Alsoforeach
would have to be qualified, since ::tcl::array::foreach is about to become available (in 8.7, if I am right)? See also my complementary answer.
– mrcalvin
Nov 19 '18 at 21:03
Just to close this loop, TP 421 implementsarray for
-- core.tcl.tk/tcl/info/7c614b93309da90a
– glenn jackman
Nov 20 '18 at 13:54
Correct, I was confused by the progenitor discussion on the corresponding bounty: github.com/flightaware/Tcl-bounties/issues/27
– mrcalvin
Nov 20 '18 at 14:14
add a comment |
As a follow-up on Schelte's answer: Alsoforeach
would have to be qualified, since ::tcl::array::foreach is about to become available (in 8.7, if I am right)? See also my complementary answer.
– mrcalvin
Nov 19 '18 at 21:03
Just to close this loop, TP 421 implementsarray for
-- core.tcl.tk/tcl/info/7c614b93309da90a
– glenn jackman
Nov 20 '18 at 13:54
Correct, I was confused by the progenitor discussion on the corresponding bounty: github.com/flightaware/Tcl-bounties/issues/27
– mrcalvin
Nov 20 '18 at 14:14
As a follow-up on Schelte's answer: Also
foreach
would have to be qualified, since ::tcl::array::foreach is about to become available (in 8.7, if I am right)? See also my complementary answer.– mrcalvin
Nov 19 '18 at 21:03
As a follow-up on Schelte's answer: Also
foreach
would have to be qualified, since ::tcl::array::foreach is about to become available (in 8.7, if I am right)? See also my complementary answer.– mrcalvin
Nov 19 '18 at 21:03
Just to close this loop, TP 421 implements
array for
-- core.tcl.tk/tcl/info/7c614b93309da90a– glenn jackman
Nov 20 '18 at 13:54
Just to close this loop, TP 421 implements
array for
-- core.tcl.tk/tcl/info/7c614b93309da90a– glenn jackman
Nov 20 '18 at 13:54
Correct, I was confused by the progenitor discussion on the corresponding bounty: github.com/flightaware/Tcl-bounties/issues/27
– mrcalvin
Nov 20 '18 at 14:14
Correct, I was confused by the progenitor discussion on the corresponding bounty: github.com/flightaware/Tcl-bounties/issues/27
– mrcalvin
Nov 20 '18 at 14:14
add a comment |
3 Answers
3
active
oldest
votes
Your set values {}
command executes in the ::tcl::array namespace, so it runs the ::tcl::array::set command. In other words, it does the equivalent of array set values {}
. So it makes values an array with no members. Then the lappend values $value
command fails because values is an array at that point.
The solution should be to use ::set values {}
Or you can completely avoid the issue by using:
proc array_values {arrayName} {
upvar 1 $arrayName ary
return [lmap {name value} [get ary] {string cat $value}]
}
add a comment |
I would like to add that, given that the presence of possibly conflicting ensemble commands is a moving target, patching an ensemble is likely to occur from everywhere, I have seen core developers keep extra ensemble commands outside the ::tcl::array::*
namespace:
proc arrayValues {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
# implant "arrayValues" into [array] ensemble as "values"
namespace ensemble configure ::array -map
[dict replace [namespace ensemble configure ::array -map]
values [namespace which arrayValues]]
This way, you don't have to worry about unintended resolution conflicts (whatever that means in Tcl, to begin with).
Just figured that you had that sorted out anyways (::array_values
).
– mrcalvin
Nov 19 '18 at 22:18
add a comment |
For the curious, this is what I have ended up with:
$HOME/tcl/lib/monkeypatches/monkeypatches.tcl
# a set of useful additions to built-in ensembles
package provide monkeypatches 0.1
namespace eval ::monkeypatches {
# https://wiki.tcl-lang.org/page/wrapping+commands
proc append_subcommand {cmd subcmd procname} {
set map [namespace ensemble configure ::$cmd -map]
dict set map $subcmd [namespace which $procname]
namespace ensemble configure ::$cmd -map $map
}
# array foreach
# to be subsumed by https://core.tcl.tk/tips/doc/trunk/tip/421.md
#
# example:
# array set A {foo bar baz qux}
# array foreach {key val} A {puts "name=$key, value=$val"}
#
proc array_foreach {vars arrayName body} {
if {[llength $vars] != 2} {
error {array foreach: "vars" must be a 2 element list}
}
lassign $vars keyVar valueVar
# Using the complicated `upvar 1 $arrayName $arrayName` so that any
# error messages propagate up with the user's array name
upvar 1 $arrayName $arrayName
$keyVar key
$valueVar value
set sid [array startsearch $arrayName]
# If the array is modified while a search is ongoing, the searchID will
# be invalidated: wrap the commands that use $sid in a try block.
try {
while {[array anymore $arrayName $sid]} {
set key [array nextelement $arrayName $sid]
set value [set "${arrayName}($key)"]
uplevel 1 $body
}
array donesearch $arrayName $sid
} trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
return -options $e "detected attempt to modify the array while iterating"
}
return
}
append_subcommand array foreach array_foreach
# array values arrayName
# https://stackoverflow.com/q/53379995/7552
#
# example:
# array set x {foo bar baz qux}
# array get x ;# => foo bar baz qux
# array names x ;# => foo baz
# array values x ;# => bar qux
#
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values [list]
array foreach {name value} ary {lappend values $value}
return $values
}
append_subcommand array values array_values
# info formalargs procName
# https://core.tcl.tk/tips/doc/trunk/tip/65.md
#
# example:
# proc test {one {two 2} {three {3 4 5}} args} {return}
# info args test ;# => one two three args
# info formalargs test ;# => one {two 2} {three {3 4 5}} args
#
proc info_formalargs {procname} {
# [info args] throws an error if $procname is not a procedure.
return [lmap arg [info args $procname] {
set has_d [info default $procname $arg value]
if {$has_d} then {list $arg $value} else {set arg}
}]
}
append_subcommand info formalargs info_formalargs
}
With its associated pkgIndex.tcl
And $HOME/.tclshrc
set lib_dir [file join $env(HOME) tcl lib]
if {$lib_dir ni $auto_path} {lappend auto_path $lib_dir}
unset lib_dir
package require monkeypatches
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53379995%2fproblem-creating-new-command-in-array-ensemble%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Your set values {}
command executes in the ::tcl::array namespace, so it runs the ::tcl::array::set command. In other words, it does the equivalent of array set values {}
. So it makes values an array with no members. Then the lappend values $value
command fails because values is an array at that point.
The solution should be to use ::set values {}
Or you can completely avoid the issue by using:
proc array_values {arrayName} {
upvar 1 $arrayName ary
return [lmap {name value} [get ary] {string cat $value}]
}
add a comment |
Your set values {}
command executes in the ::tcl::array namespace, so it runs the ::tcl::array::set command. In other words, it does the equivalent of array set values {}
. So it makes values an array with no members. Then the lappend values $value
command fails because values is an array at that point.
The solution should be to use ::set values {}
Or you can completely avoid the issue by using:
proc array_values {arrayName} {
upvar 1 $arrayName ary
return [lmap {name value} [get ary] {string cat $value}]
}
add a comment |
Your set values {}
command executes in the ::tcl::array namespace, so it runs the ::tcl::array::set command. In other words, it does the equivalent of array set values {}
. So it makes values an array with no members. Then the lappend values $value
command fails because values is an array at that point.
The solution should be to use ::set values {}
Or you can completely avoid the issue by using:
proc array_values {arrayName} {
upvar 1 $arrayName ary
return [lmap {name value} [get ary] {string cat $value}]
}
Your set values {}
command executes in the ::tcl::array namespace, so it runs the ::tcl::array::set command. In other words, it does the equivalent of array set values {}
. So it makes values an array with no members. Then the lappend values $value
command fails because values is an array at that point.
The solution should be to use ::set values {}
Or you can completely avoid the issue by using:
proc array_values {arrayName} {
upvar 1 $arrayName ary
return [lmap {name value} [get ary] {string cat $value}]
}
edited Nov 19 '18 at 19:48
answered Nov 19 '18 at 19:42
Schelte Bron
463128
463128
add a comment |
add a comment |
I would like to add that, given that the presence of possibly conflicting ensemble commands is a moving target, patching an ensemble is likely to occur from everywhere, I have seen core developers keep extra ensemble commands outside the ::tcl::array::*
namespace:
proc arrayValues {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
# implant "arrayValues" into [array] ensemble as "values"
namespace ensemble configure ::array -map
[dict replace [namespace ensemble configure ::array -map]
values [namespace which arrayValues]]
This way, you don't have to worry about unintended resolution conflicts (whatever that means in Tcl, to begin with).
Just figured that you had that sorted out anyways (::array_values
).
– mrcalvin
Nov 19 '18 at 22:18
add a comment |
I would like to add that, given that the presence of possibly conflicting ensemble commands is a moving target, patching an ensemble is likely to occur from everywhere, I have seen core developers keep extra ensemble commands outside the ::tcl::array::*
namespace:
proc arrayValues {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
# implant "arrayValues" into [array] ensemble as "values"
namespace ensemble configure ::array -map
[dict replace [namespace ensemble configure ::array -map]
values [namespace which arrayValues]]
This way, you don't have to worry about unintended resolution conflicts (whatever that means in Tcl, to begin with).
Just figured that you had that sorted out anyways (::array_values
).
– mrcalvin
Nov 19 '18 at 22:18
add a comment |
I would like to add that, given that the presence of possibly conflicting ensemble commands is a moving target, patching an ensemble is likely to occur from everywhere, I have seen core developers keep extra ensemble commands outside the ::tcl::array::*
namespace:
proc arrayValues {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
# implant "arrayValues" into [array] ensemble as "values"
namespace ensemble configure ::array -map
[dict replace [namespace ensemble configure ::array -map]
values [namespace which arrayValues]]
This way, you don't have to worry about unintended resolution conflicts (whatever that means in Tcl, to begin with).
I would like to add that, given that the presence of possibly conflicting ensemble commands is a moving target, patching an ensemble is likely to occur from everywhere, I have seen core developers keep extra ensemble commands outside the ::tcl::array::*
namespace:
proc arrayValues {arrayName} {
upvar 1 $arrayName ary
set values {}
foreach {name value} [array get ary] {lappend values $value}
return $values
}
# implant "arrayValues" into [array] ensemble as "values"
namespace ensemble configure ::array -map
[dict replace [namespace ensemble configure ::array -map]
values [namespace which arrayValues]]
This way, you don't have to worry about unintended resolution conflicts (whatever that means in Tcl, to begin with).
edited Nov 19 '18 at 21:27
answered Nov 19 '18 at 21:11
mrcalvin
1,169612
1,169612
Just figured that you had that sorted out anyways (::array_values
).
– mrcalvin
Nov 19 '18 at 22:18
add a comment |
Just figured that you had that sorted out anyways (::array_values
).
– mrcalvin
Nov 19 '18 at 22:18
Just figured that you had that sorted out anyways (
::array_values
).– mrcalvin
Nov 19 '18 at 22:18
Just figured that you had that sorted out anyways (
::array_values
).– mrcalvin
Nov 19 '18 at 22:18
add a comment |
For the curious, this is what I have ended up with:
$HOME/tcl/lib/monkeypatches/monkeypatches.tcl
# a set of useful additions to built-in ensembles
package provide monkeypatches 0.1
namespace eval ::monkeypatches {
# https://wiki.tcl-lang.org/page/wrapping+commands
proc append_subcommand {cmd subcmd procname} {
set map [namespace ensemble configure ::$cmd -map]
dict set map $subcmd [namespace which $procname]
namespace ensemble configure ::$cmd -map $map
}
# array foreach
# to be subsumed by https://core.tcl.tk/tips/doc/trunk/tip/421.md
#
# example:
# array set A {foo bar baz qux}
# array foreach {key val} A {puts "name=$key, value=$val"}
#
proc array_foreach {vars arrayName body} {
if {[llength $vars] != 2} {
error {array foreach: "vars" must be a 2 element list}
}
lassign $vars keyVar valueVar
# Using the complicated `upvar 1 $arrayName $arrayName` so that any
# error messages propagate up with the user's array name
upvar 1 $arrayName $arrayName
$keyVar key
$valueVar value
set sid [array startsearch $arrayName]
# If the array is modified while a search is ongoing, the searchID will
# be invalidated: wrap the commands that use $sid in a try block.
try {
while {[array anymore $arrayName $sid]} {
set key [array nextelement $arrayName $sid]
set value [set "${arrayName}($key)"]
uplevel 1 $body
}
array donesearch $arrayName $sid
} trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
return -options $e "detected attempt to modify the array while iterating"
}
return
}
append_subcommand array foreach array_foreach
# array values arrayName
# https://stackoverflow.com/q/53379995/7552
#
# example:
# array set x {foo bar baz qux}
# array get x ;# => foo bar baz qux
# array names x ;# => foo baz
# array values x ;# => bar qux
#
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values [list]
array foreach {name value} ary {lappend values $value}
return $values
}
append_subcommand array values array_values
# info formalargs procName
# https://core.tcl.tk/tips/doc/trunk/tip/65.md
#
# example:
# proc test {one {two 2} {three {3 4 5}} args} {return}
# info args test ;# => one two three args
# info formalargs test ;# => one {two 2} {three {3 4 5}} args
#
proc info_formalargs {procname} {
# [info args] throws an error if $procname is not a procedure.
return [lmap arg [info args $procname] {
set has_d [info default $procname $arg value]
if {$has_d} then {list $arg $value} else {set arg}
}]
}
append_subcommand info formalargs info_formalargs
}
With its associated pkgIndex.tcl
And $HOME/.tclshrc
set lib_dir [file join $env(HOME) tcl lib]
if {$lib_dir ni $auto_path} {lappend auto_path $lib_dir}
unset lib_dir
package require monkeypatches
add a comment |
For the curious, this is what I have ended up with:
$HOME/tcl/lib/monkeypatches/monkeypatches.tcl
# a set of useful additions to built-in ensembles
package provide monkeypatches 0.1
namespace eval ::monkeypatches {
# https://wiki.tcl-lang.org/page/wrapping+commands
proc append_subcommand {cmd subcmd procname} {
set map [namespace ensemble configure ::$cmd -map]
dict set map $subcmd [namespace which $procname]
namespace ensemble configure ::$cmd -map $map
}
# array foreach
# to be subsumed by https://core.tcl.tk/tips/doc/trunk/tip/421.md
#
# example:
# array set A {foo bar baz qux}
# array foreach {key val} A {puts "name=$key, value=$val"}
#
proc array_foreach {vars arrayName body} {
if {[llength $vars] != 2} {
error {array foreach: "vars" must be a 2 element list}
}
lassign $vars keyVar valueVar
# Using the complicated `upvar 1 $arrayName $arrayName` so that any
# error messages propagate up with the user's array name
upvar 1 $arrayName $arrayName
$keyVar key
$valueVar value
set sid [array startsearch $arrayName]
# If the array is modified while a search is ongoing, the searchID will
# be invalidated: wrap the commands that use $sid in a try block.
try {
while {[array anymore $arrayName $sid]} {
set key [array nextelement $arrayName $sid]
set value [set "${arrayName}($key)"]
uplevel 1 $body
}
array donesearch $arrayName $sid
} trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
return -options $e "detected attempt to modify the array while iterating"
}
return
}
append_subcommand array foreach array_foreach
# array values arrayName
# https://stackoverflow.com/q/53379995/7552
#
# example:
# array set x {foo bar baz qux}
# array get x ;# => foo bar baz qux
# array names x ;# => foo baz
# array values x ;# => bar qux
#
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values [list]
array foreach {name value} ary {lappend values $value}
return $values
}
append_subcommand array values array_values
# info formalargs procName
# https://core.tcl.tk/tips/doc/trunk/tip/65.md
#
# example:
# proc test {one {two 2} {three {3 4 5}} args} {return}
# info args test ;# => one two three args
# info formalargs test ;# => one {two 2} {three {3 4 5}} args
#
proc info_formalargs {procname} {
# [info args] throws an error if $procname is not a procedure.
return [lmap arg [info args $procname] {
set has_d [info default $procname $arg value]
if {$has_d} then {list $arg $value} else {set arg}
}]
}
append_subcommand info formalargs info_formalargs
}
With its associated pkgIndex.tcl
And $HOME/.tclshrc
set lib_dir [file join $env(HOME) tcl lib]
if {$lib_dir ni $auto_path} {lappend auto_path $lib_dir}
unset lib_dir
package require monkeypatches
add a comment |
For the curious, this is what I have ended up with:
$HOME/tcl/lib/monkeypatches/monkeypatches.tcl
# a set of useful additions to built-in ensembles
package provide monkeypatches 0.1
namespace eval ::monkeypatches {
# https://wiki.tcl-lang.org/page/wrapping+commands
proc append_subcommand {cmd subcmd procname} {
set map [namespace ensemble configure ::$cmd -map]
dict set map $subcmd [namespace which $procname]
namespace ensemble configure ::$cmd -map $map
}
# array foreach
# to be subsumed by https://core.tcl.tk/tips/doc/trunk/tip/421.md
#
# example:
# array set A {foo bar baz qux}
# array foreach {key val} A {puts "name=$key, value=$val"}
#
proc array_foreach {vars arrayName body} {
if {[llength $vars] != 2} {
error {array foreach: "vars" must be a 2 element list}
}
lassign $vars keyVar valueVar
# Using the complicated `upvar 1 $arrayName $arrayName` so that any
# error messages propagate up with the user's array name
upvar 1 $arrayName $arrayName
$keyVar key
$valueVar value
set sid [array startsearch $arrayName]
# If the array is modified while a search is ongoing, the searchID will
# be invalidated: wrap the commands that use $sid in a try block.
try {
while {[array anymore $arrayName $sid]} {
set key [array nextelement $arrayName $sid]
set value [set "${arrayName}($key)"]
uplevel 1 $body
}
array donesearch $arrayName $sid
} trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
return -options $e "detected attempt to modify the array while iterating"
}
return
}
append_subcommand array foreach array_foreach
# array values arrayName
# https://stackoverflow.com/q/53379995/7552
#
# example:
# array set x {foo bar baz qux}
# array get x ;# => foo bar baz qux
# array names x ;# => foo baz
# array values x ;# => bar qux
#
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values [list]
array foreach {name value} ary {lappend values $value}
return $values
}
append_subcommand array values array_values
# info formalargs procName
# https://core.tcl.tk/tips/doc/trunk/tip/65.md
#
# example:
# proc test {one {two 2} {three {3 4 5}} args} {return}
# info args test ;# => one two three args
# info formalargs test ;# => one {two 2} {three {3 4 5}} args
#
proc info_formalargs {procname} {
# [info args] throws an error if $procname is not a procedure.
return [lmap arg [info args $procname] {
set has_d [info default $procname $arg value]
if {$has_d} then {list $arg $value} else {set arg}
}]
}
append_subcommand info formalargs info_formalargs
}
With its associated pkgIndex.tcl
And $HOME/.tclshrc
set lib_dir [file join $env(HOME) tcl lib]
if {$lib_dir ni $auto_path} {lappend auto_path $lib_dir}
unset lib_dir
package require monkeypatches
For the curious, this is what I have ended up with:
$HOME/tcl/lib/monkeypatches/monkeypatches.tcl
# a set of useful additions to built-in ensembles
package provide monkeypatches 0.1
namespace eval ::monkeypatches {
# https://wiki.tcl-lang.org/page/wrapping+commands
proc append_subcommand {cmd subcmd procname} {
set map [namespace ensemble configure ::$cmd -map]
dict set map $subcmd [namespace which $procname]
namespace ensemble configure ::$cmd -map $map
}
# array foreach
# to be subsumed by https://core.tcl.tk/tips/doc/trunk/tip/421.md
#
# example:
# array set A {foo bar baz qux}
# array foreach {key val} A {puts "name=$key, value=$val"}
#
proc array_foreach {vars arrayName body} {
if {[llength $vars] != 2} {
error {array foreach: "vars" must be a 2 element list}
}
lassign $vars keyVar valueVar
# Using the complicated `upvar 1 $arrayName $arrayName` so that any
# error messages propagate up with the user's array name
upvar 1 $arrayName $arrayName
$keyVar key
$valueVar value
set sid [array startsearch $arrayName]
# If the array is modified while a search is ongoing, the searchID will
# be invalidated: wrap the commands that use $sid in a try block.
try {
while {[array anymore $arrayName $sid]} {
set key [array nextelement $arrayName $sid]
set value [set "${arrayName}($key)"]
uplevel 1 $body
}
array donesearch $arrayName $sid
} trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
return -options $e "detected attempt to modify the array while iterating"
}
return
}
append_subcommand array foreach array_foreach
# array values arrayName
# https://stackoverflow.com/q/53379995/7552
#
# example:
# array set x {foo bar baz qux}
# array get x ;# => foo bar baz qux
# array names x ;# => foo baz
# array values x ;# => bar qux
#
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values [list]
array foreach {name value} ary {lappend values $value}
return $values
}
append_subcommand array values array_values
# info formalargs procName
# https://core.tcl.tk/tips/doc/trunk/tip/65.md
#
# example:
# proc test {one {two 2} {three {3 4 5}} args} {return}
# info args test ;# => one two three args
# info formalargs test ;# => one {two 2} {three {3 4 5}} args
#
proc info_formalargs {procname} {
# [info args] throws an error if $procname is not a procedure.
return [lmap arg [info args $procname] {
set has_d [info default $procname $arg value]
if {$has_d} then {list $arg $value} else {set arg}
}]
}
append_subcommand info formalargs info_formalargs
}
With its associated pkgIndex.tcl
And $HOME/.tclshrc
set lib_dir [file join $env(HOME) tcl lib]
if {$lib_dir ni $auto_path} {lappend auto_path $lib_dir}
unset lib_dir
package require monkeypatches
edited Nov 20 '18 at 13:45
answered Nov 20 '18 at 2:28
glenn jackman
166k26143234
166k26143234
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53379995%2fproblem-creating-new-command-in-array-ensemble%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
As a follow-up on Schelte's answer: Also
foreach
would have to be qualified, since ::tcl::array::foreach is about to become available (in 8.7, if I am right)? See also my complementary answer.– mrcalvin
Nov 19 '18 at 21:03
Just to close this loop, TP 421 implements
array for
-- core.tcl.tk/tcl/info/7c614b93309da90a– glenn jackman
Nov 20 '18 at 13:54
Correct, I was confused by the progenitor discussion on the corresponding bounty: github.com/flightaware/Tcl-bounties/issues/27
– mrcalvin
Nov 20 '18 at 14:14