Merge sorts

2009-03-18 kruzalex I decided to put some code in conjuncion with merge sorts playing little bit with PHP array functionality, cause I didnt see any on wiki pages in this relation. Below rewrited sorting procs are mainly sourced from existing php codes...

#---------Merge sort

rename array Array

namespace eval array {
 variable info [list]
}

proc array {args} {
     set res [info level 0]
     set array_name [lindex [info level 0] 2]

     if {[lindex $args 0] == "exists"} {

         return [uplevel 1 [list Array exists $array_name]]   
     } elseif {[lindex $args 0] == "set"} {

         if {[regexp {[[:digit:][:alpha:]][(][0-9]+[)]} [lindex $args 1]] != 1} {

             set ::array::temp [list]
             for {set x 0} {$x<[llength  [lindex $res 3]]} {incr x} {
                 lappend ::array::temp $x [lindex [lindex $res 3] $x]
             } 
             uplevel 1 [list Array unset $array_name]
             uplevel 1 [list Array set $array_name $::array::temp]           

         } elseif {[regexp {[[:digit:][:alpha:]][(][0-9]+[)]} [lindex $args 1]] == 1} {

            regexp {[[:digit:][:alpha:]]+} [lindex $args 1] array_name
            regexp {[(](.*?)[)]} [lindex $args 1] -> array_key        
            uplevel 1 [list set ${array_name}($array_key) [lindex $args 2]]
         }    
    } elseif {[lindex $args 0] == "get"} {
        return [uplevel 1 [list Array get $array_name]]
    } elseif {[lindex $args 0] == "size"} {
        return [uplevel 1 [list Array size $array_name]]
    } elseif {[lindex $args 0] == "unset"} {

        if {[regexp {[[:digit:][:alpha:]][(][0-9]+[)]} [lindex $args 1]] == 1} {
            regexp {[[:digit:][:alpha:]]+} [lindex $args 1] array_name
            regexp {[(](.*?)[)]} [lindex $args 1] -> array_key
            uplevel 1 [list unset ${array_name}($array_key)]
        } elseif {[regexp {[[:digit:][:alpha:]]} [lindex $args 1]] == 1} {
            uplevel 1 [list Array unset $array_name]
        }
    }
}

proc array_shift { arry args } {
    upvar $arry a
    set array_old [list]
    set array_new [list]
    foreach {key value} [Array get a] {
        lappend array_old [list $key $value]  
    }    
    set first_key [lindex [lsort -index 0 $array_old] 0 0]
    set shifted $a($first_key)
    set n 0
    foreach {key value} [eval concat [lsort -index 0 $array_old]] {
        if {$key!=$first_key} {
            lappend array_new $value          
        }    
    }
    uplevel 1 [list array set $arry $array_new]
    return $shifted
}

proc array_pop { arry args } {
    upvar $arry a
    set array_new [list]
    foreach {key value} [Array get a] {
        lappend array_new [list $key $value]  
    }    
    set last_key [lindex [lsort -index 0 $array_new] end 0]
    set shifted $a($last_key)
    uplevel 1 [list array unset ${arry}($last_key)]]
    return $shifted
}

proc array_unshift { arry args } {
    upvar $arry a
    set array_old [list]
    set array_new $args
    foreach {key value} [Array get a] {
        lappend array_old [list $key $value]  
    }    
    foreach {key value} [eval concat [lsort -index 0 $array_old]] {
        lappend array_new $value         
    }
    uplevel 1 [list array set $arry $array_new]
    return [uplevel 1 [list array size $arry]]
}

proc array_push { arry args } {
    upvar $arry a
    set array_old [list]
    foreach {key value} [Array get a] {
        lappend array_old $value
    }
    set array_new [eval concat [lappend array_old $args]]
    uplevel 1 [list array set $arry $array_new]
    return [uplevel 1 [list array size $arry]]
}

proc array_first_value {arry} {
    upvar $arry a
    set first_value $a([uplevel 1 [list array_first_key $arry]])
    return $first_value
}
   
proc array_last_value {arry} {
    upvar $arry a
    set last_value $a([uplevel 1 [list array_last_key $arry]])
    return $last_value
}
   
proc array_first_key {arry} {
    upvar $arry a
    set array_new [list]
    foreach {key value} [Array get a] {
        lappend array_new [list $key $value]  
    }    
    set first_key [lindex [lsort -index 0 $array_new] 0 0]
    return $first_key
}
   
proc array_last_key {arry} {
    upvar $arry a
    set array_new [list]
    foreach {key value} [Array get a] {
        lappend array_new [list $key $value]  
    }    
    set last_key [lindex [lsort -index 0 $array_new] end 0]
    return $last_key
}
   
proc array_splice {arry offset args} {
    upvar $arry a    
    set array_old [list]
    set array_new [list]
    set replacement [lrange $args 1 end]
    foreach {key value} [Array get a] {
        lappend array_old [list $key $value]
    }

    foreach {key value} [eval concat [lsort -index 0 $array_old]] {
        lappend array_new $value    
    } 
    if {[llength $args]>0} {
        set length [lindex $args 0]
        if {$length<0} {
            if {$offset<0} {
                return $array_new
            } else {

                puts [lindex $replacement 0]
                puts [lindex [lindex $replacement 0] 0]
                puts [lindex [lindex [lindex $replacement 0] 0] 0]
                set array_left [lrange $array_new 0 [expr $offset-1]]
                set array_middle [lrange $array_new end-[expr ($length*-1)-1] end-[expr ($length*-1)-1]]
                set array_right [lrange $array_new end-[expr ($length*-1)-2] end]
                if {[llength $replacement]>0} {
                    set array_new [concat $array_left $replacement $array_right]
                } else {
                    set array_new [concat $array_left $array_middle $array_right]            
                }
            }
        } else {
        if {$offset<0} {
            set array_left [lrange $array_new 0 end-[expr ($offset*-1)]]
            set array_right [lrange $array_new end-[expr ($offset*-1)-1-$length] end]
            set array_new [concat $array_left $replacement $array_right]    
        } else {    
            set array_left [lrange $array_new 0 [expr $offset-1]]
            set array_right [lrange $array_new [expr $offset+$length] end]
            set array_new [concat $array_left $replacement $array_right]
        } 
        }
    } else {
        if {$offset<0} {
            set array_new [lrange $array_new 0 end-[expr ($offset*-1)]]
        } else {  
            set array_new [lrange $array_new 0 [expr $offset-1]]
        }
    }
    uplevel 1 [list array set $arry $array_new]    
}

proc array_slice {arry offset args} {
    upvar $arry a    
    set array_old [list]
    set array_new [list]

    foreach {key value} [Array get a] {
        lappend array_old [list $key $value]
    }

    foreach {key value} [eval concat [lsort -index 0 $array_old]] {
        lappend array_new $value    
    } 

    if {[llength $args]>0} {
        set length [lindex $args 0]
        if {$offset<0} {
            set array_new [lrange $array_new [expr [llength $array_new]-[expr ($offset*-1)]] [expr [llength    $array_new]-[expr ($offset*-1)+1]+$length]]
        } else {    
            set array_new [lrange $array_new $offset [expr $offset+$length-1]]    
        }
    } else {
        set array_new [lrange $array_new $offset end]
    }
    return $array_new
}

proc mergeSort {args} {
    array set external [list]
    array set inputArray $args
    while {[array size inputArray]>0} {
        array set array1 [array_splice external 0]
        array set array2 [array_shift inputArray]
        array set output [list]
        while {[array size array1]>0 && [array size array2]>0} {
            if {$array1(0) < $array2(0)} {
                array_push output [array_shift array1]
            } else {
                array_push output [array_shift array2] 
            }        
        }

        while {[array size array1]>0} {
            array_push output [array_shift array1]
        }

        while {[array size array2]>0} {
            array_push output [array_shift array2]
        }

        foreach {key value} [array get output] {
            lappend temp $value          
        }

        array set external $temp
    }

    foreach {key value} [lsort -index 0 [array get external]] {
        lappend result $value
    }
    return $result
}

puts [mergeSort 3 1 5 4 2]

Strand sort

proc strandSort {args} {
    array set results [list]
    array set arr $args
    while {[array size arr]>0} {
        array set sublist [array_shift arr]
        set last [array_last_key sublist]
        foreach {i val} [array get arr] {
            if {$val > $sublist($last)} {  
            array_push sublist $val
            array unset arr($i)
            incr last
            }           
        }
        if {[array size result]>0} {
            foreach {key val} [array get sublist] {
                set spliced false
                foreach {i rval} [array get result] {
                    if {$val < $rval} {
                        array_splice result $i 0 $val
                        set spliced true
                        break                          
                    }         
                }
                if {!$spliced} {
                    array_push result $val
                }
            }
        } else {

            foreach {key value} [array get sublist] {
                lappend temp $value          
            }
            array set result $temp
            unset temp
        }
    }
    foreach {key value} [lsort -index 0 [array get result]] {
        lappend res $value
    }
    return $res
}

puts [strandSort 3 1 5 4 2]

glennj another merge sort

    package require Tcl 8.5

    proc mergesort m {
        set len [llength $m]
        if {$len <= 1} {
            return $m
        }
        set middle [expr {$len / 2}]
        set left [lrange $m 0 [expr {$middle - 1}]]
        set right [lrange $m $middle end]
        return [merge [mergesort $left] [mergesort $right]]
    }

    proc merge {left right} {
        set result [list]
        while { [set lleft  [llength $left]]  > 0 &&
                [set lright [llength $right]] > 0
        } {
            if {[lindex $left 0] <= [lindex $right 0]} {
                set left [lassign $left value]
                lappend result $value
            } else {
                set right [lassign $right value]
                lappend result $value
            }
        }
        if {$lleft > 0} {
            lappend result {*}$left
        }
        if {$lright > 0} {
            set result [concat $result $right] ;# another way append elements
        }
        return $result
    }

    puts [mergesort {8 6 4 2 1 3 5 7 9}] ;# => 1 2 3 4 5 6 7 8 9