Please help.
Cheers,
iand
yes, there will be a 'lrem' command as soon as you write the procedure
which wraps the required lreplace etc command(s) -- that's what
extensibility is all about ;)
The idea you are asking about is the ability to say "delete members 1
3 5 and 7 of list xyz"?
I don't know of a specific variety of lreplace to do that. However,
it seems to me that something like this pseudo code would be possible
proc lrem { inList args } {
if { ! [string equal [lindex $args 0] "-indices"]} {
puts stderr "USAGE: $::argv0 list -indices {indexlist}"
return {}
}
set indices [lrange $args 1 end]
puts "indices = $indices"
set sortedindices [ lsort -integer -decreasing {*}$indices]
puts "sortedindices = $sortedindices"
foreach i $sortedindices {
puts "i = $i"
set inList [lreplace $inList $i $i]
puts "inList = $inList"
}
puts "returning inList = $inList"
return $inList
}
set l [list 1 a 2 b 3 c 4 d]
set rc [lrem $l -indices [list 2 4 6]]
puts "rc = $rc"
I think there was a wrapper proc introduce in a book. The proc name
was 'ldelete'..
Here is a version which takes care of a number of potential problems:
# Note: lranges returns potentially overlapping ranges
proc ::lranges { listVar {lRanges {{0 end}}} } {
upvar $listVar inList
set outList [list]
foreach range $lRanges {
lappend outList {*}[lrange $inList {*}$range]
}
return $outList
}
# lremove removes ranges from list by
# first inverting the list, then using lranges.
# Ugly, but seems to work:
proc ::lremove { listVar {lRanges {{0 end}}} } {
upvar $listVar inList
set endIndex [expr {[llength $inList] - 1}]
set rBegin 0
set rEnd $endIndex
set outRanges [list]
puts "lRanges = $lRanges"
foreach range [lsort -integer -index 0\
[lsort -integer -index 1 -decreasing\
[string map [list end $endIndex] $lRanges]]] {
set rangeBegin [expr [lindex $range 0]]
set rangeEnd [expr [lindex $range 1]]
puts "range = '$range', rBegin = $rBegin, rEnd = $rEnd, rangeBegin =
$rangeBegin rangeEnd = $rangeEnd"
if {$rangeBegin == $rBegin} {
set rBegin [expr $rangeEnd+1]
lappend begin $rBegin
lappend end $rEnd
continue
}
if {$rangeEnd == $rEnd} {
set rEnd [expr $rangeBegin-1]
lappend begin $rBegin
lappend end $rEnd
continue
}
lappend begin $rBegin
lappend end [expr $rangeBegin -1]
lappend begin [expr $rangeEnd+1]
lappend end $rEnd
}
set begin [lsort -integer -unique $begin]
set end [lsort -integer -unique $end]
puts "begin = $begin, end = $end"
puts "rBegin = $rBegin, rEnd = $rEnd"
set i 0
foreach beg $begin {
if {$beg < $rBegin} {
set beg $rBegin
}
set en [lindex $end $i]
if {$en eq ""} {
break
} elseif {$en > $rEnd} {
set en $rEnd
}
lappend outRanges [list $beg $en]
incr i
}
puts "outRanges = [lsort -unique $outRanges]"
#return [lsort -unique $outRanges]
return [lranges inList [lsort -unique $outRanges]]
}
# Example:
set list [list a b c d e f g h i j]
lremove list [list {0 5}]
lremove list [list {0 2}]
lremove list [list {0 2} {3 5}]
puts "[lremove list {{3 5} {0 2} {8 9}}]"
# should return "g h" as a two element list.
Note that [lremove] works with "end", but not with "end-1", etc.
sincerely asking for such list elements deletion commands in the core
is always told against, that there is "lreplace" and that, if really,
really needed everybody can do his own lremove, ldelete, etc. proc
based on lreplace. The reason ... the core shouldn't be bloated by
unnecessary crap.
So in my own list commands extension package are some procs caring for
deleting "single" elements or (all) elements matching specified
patterns/contents.
But they all are inefficient or are misusing other functionality.
Example:
# delete an element to be found
set list [lreplace $list [set idx [lsearch $list $element]] $idx];
set list [lsearch -inline -all -not -exact $list $element];
The first line uses temporary tcl variables, the second misuses
lsearch for deletion.
Example:
# delete specified elements
foreach idx [lreverse $idxList] {
set list [lreplace $list $idx $idx];
}
This example code is so normal, so that nobody will argue, but doing
this in core would be a very good optimization. And ... not that long
ago "lreverse" was coded by my own, because it wasn't in the core,
too!
Why everybody has to care for the same reoccurring problem - how to
delete specific elements from a list, without having to build a new
proc.
Why this standard use case is not part of the core?
This discussion is very old, because it can be extended about such
functionality of the set theory - creating unions, intersections,
etc..
There is no build in functionality like "lunion", "lintersect", etc. -
not for lists and not for dicts. There is no maths on list( element)s.
I don't want to extend this discussion too much, but I would be really
glad, if not everybody needs a tcllib or own packages to extend basic
list functionality.
In my eyes adding lreverse was not bloating the core and adding (via a
TIP) lremove or ldelete wouldn't either bloat the core.
Best regards,
Martin
P.S.: sometimes I feel like having to reinvent the wheel using tcl,
just to provide such basic stuff for "internal" data structures. So I
have my own library for lists, strings, ... . But I always found this
a bit annoying, even feeling glad about this possibility to extend tcl
that easily.
Most of our list manipulation commands don't take any operations; they
do what they do and just do that. There isn't a command to remove
items at indices (perhaps there should be, but there isn't) but as you
can see from this thread, it's pretty easy to add one if only numeric
indices are supported. Supporting non-numeric (i.e., end-relative)
indices would be better done from C, and that might actually be a
reasonable case for us to add an [lremove] command in the future.
Hmm. Ought to TIP that...
Donal.
Done. See http://tip.tcl.tk/367.html for a more detailed plan. No time
schedule for it yet.
Donal.
> P.S.: sometimes I feel like having to reinvent the wheel using tcl,
> just to provide such basic stuff for "internal" data structures. So I
> have my own library for lists, strings, ... . But I always found this
> a bit annoying, even feeling glad about this possibility to extend tcl
> that easily.
>
Note that the tcllib distribution (http://tcllib.sf.net/) is a set of
useful Tcl procs that developers have contributed. Perhaps it would be
worthwhile to add your library to tcllib. That distribution is pretty
common for people to have available in their environment.
I guess TIPs are discussed on the Tcl core, but I would just like to
add here that it would be nice to move away from commands which
require using expand syntax {*} and use list syntax for the list of
indices. Note that a list is what is now returned from new commands
like [lsearch -subidices -index ...]. This should also make it much
easier to create byte-code for the command. I propose something
slightly different from my example above:
lremove ?-indexRanges? listVar [list 1 {3 5} 7 9 {11 13} {end-1 end}]
I also propose a helper function, exposed as a command [lranges
listVar rangesList].
It seems to me that users would more often know which list elements or
which ranges of elements they want and creating a new list requires
either a loop to add new elements, or expand, which could be expensive
for large lists as a Tcl proc.
Is this an abstract question, or do you have a specific problem that
you are trying to solve? The reason I ask is that with more specific
explanation of the problem you want to solve, a solution for that
problem will be more likely to be able to be offered.
you are right, but I have to ask again my employer, to get his
allowance to publish code I've written some time ago during my work.
Another thing is, that I don't have currently the time to write a
"normal" tcl test suite and to introduce doctools tags for automatic
documentation.
It's not the time for doing it, but the time for learning how to do.
And at last - the sources of my library packages are only tcl 8.5+
compatible. Is this allowed for tcllib packages?
Best regards,
Martin Lemburg
> you are right, but I have to ask again my employer, to get his
> allowance to publish code I've written some time ago during my work.
That is certainly understandable and encouraged.
>
> Another thing is, that I don't have currently the time to write a
> "normal" tcl test suite and to introduce doctools tags for automatic
> documentation.
> It's not the time for doing it, but the time for learning how to do.
Perhaps, once you've received an okay to make the code available, you
could try writing up a posting or wiki page describing the library in
enough detail that it might result in recruiting some help for either
writing doc or tests.
>
> And at last - the sources of my library packages are only tcl 8.5+
> compatible. Is this allowed for tcllib packages?
>
That should not be any problem - just make certain the code has the
package require Tcl 8.5 or whatever it requires so that it doesn't try
to load in older tcl environments.