Permalink
Browse files

Add some more helper methods to underscore.tcl.

  • Loading branch information...
1 parent bdfb357 commit 30f77db3d906918bdda85b7f42b0fba5c56559a0 @arthurschreiber committed Nov 20, 2012
Showing with 175 additions and 1 deletion.
  1. +89 −1 lib/underscore.tcl
  2. +86 −0 spec/underscore_spec.tcl
View
90 lib/underscore.tcl
@@ -129,6 +129,34 @@ namespace eval _ {
return $list
}
+ # Iterates over the passed list, yielding each element and its index in turn
+ # to the passed iterator.
+ #
+ # @return [list] The given list.
+ proc each_with_index { list iterator } {
+ set count [llength $list]
+
+ for { set i 0 } { $i < [llength $list] } { incr i } {
+ _::yield $iterator $item $i
+ }
+
+ return $list
+ }
+
+ # Iterates over the passed list in slices of +number+ elements.
+ #
+ # @return An empty string.
+ proc each_slice { list number iterator } {
+ if { $number < 1 } {
+ return -code error "Invalid slice size"
+ }
+
+ for { set i 0 } { $i < [llength $list] } { incr i $number } {
+ _::yield $iterator [lrange $list $i [expr { $i+$number-1 }]]
+ }
+ return
+ }
+
# Returns a new list of values by applying the given block to each
# value of the given list.
proc map { list iterator } {
@@ -156,13 +184,73 @@ namespace eval _ {
return $result
}
- proc reduce { list iterator memo } {
+ proc reduce { list iterator args } {
+ if { [llength $args] > 1 } {
+ return -code error "Wrong # of args: should be _::reduce list iterator ?initial?"
+ }
+
+ if { [llength $args] == 1 } {
+ set memo [lindex $args 0]
+ } elseif { [llength $list] == 0 } {
+ return -code error "Reduce of empty list with no initial value"
+ } else {
+ set list [lassign $list memo]
+ }
+
foreach item $list {
set memo [_::yield $iterator $memo $item]
}
return $memo
}
+ proc reduce_right { list iterator args } {
+ if { [llength $args] > 1 } {
+ return -code error "Wrong # of args: should be _::reduce_right list iterator ?initial?"
+ }
+
+ if { [llength $args] == 1 } {
+ set memo [lindex $args 0]
+ } elseif { [llength $list] == 0 } {
+ return -code error "Reduce of empty list with no initial value"
+ } else {
+ set memo [lindex $list "end"]
+ set list [lreplace $list [set list "end"] "end"]
+ }
+
+ set length [llength $list]
+ while { $length > 0 } {
+ incr length -1
+ set memo [_::yield $iterator $memo [lindex $list $length]]
+ }
+
+ return $memo
+ }
+
+ proc find { list iterator } {
+ foreach value $list {
+ if { [_::yield $iterator $value] } {
+ return $value
+ }
+ }
+
+ # Return an empty string.
+ return
+ }
+
+ proc partition { list iterator } {
+ set first [set second [list]]
+
+ foreach value $list {
+ if { [_::yield $iterator $value] } {
+ lappend first $value
+ } else {
+ lappend second $value
+ }
+ }
+
+ list $first $second
+ }
+
# Executes the passed iterator with each element of the passed list.
# Returns true if the passed block never returns a 'falsy' value.
#
View
86 spec/underscore_spec.tcl
@@ -155,6 +155,22 @@ describe "_::each" {
}
}
+describe "_::each_slice" {
+ it "yields the given list in slices of the given size" {
+ set result [list]
+ _::each_slice [list 1 2 3 4 5 6 7 8] 3 {{ slice } {
+ upvar result result
+ lappend result $slice
+ }}
+
+ expect $result to equal [list \
+ [list 1 2 3] \
+ [list 4 5 6] \
+ [list 7 8] \
+ ]
+ }
+}
+
describe "_::map" {
it "continues iteration and uses the given value if return -code continue is called" {
expect [_::map {1 2 3 4 5} { x {
@@ -337,6 +353,76 @@ describe "_::index_of" {
}
}
+describe "_::reduce" {
+ it "can sum up a list of numbers" {
+ expect [_::reduce {1 2 3} {{ sum num } {
+ expr { $sum + $num }
+ }} 0] to equal 6
+ }
+
+ it "can access surrounding variables using upvar" {
+ set multiplier 3
+ expect [_::reduce {1 2 3} {{ sum num } {
+ upvar multiplier multiplier
+ expr { $sum + $num * $multiplier }
+ }} 0] to equal 18
+ }
+
+ it "has a default initial value" {
+ expect [_::reduce {1 2 3} {{ sum num } {
+ expr { $sum + $num }
+ }}] to equal 6
+ }
+
+ it "returns the initial value if given an empty list" {
+ expect [_::reduce {} {{ memo value } { }} 10] to equal 10
+ }
+
+ it "throws an error when given a list without an initial value" {
+ expect {
+ _::reduce {} {{ memo value } { }}
+ } to raise_error
+ }
+}
+
+describe "_::reduce_right" {
+ it "can perform right folds" {
+ expect [_::reduce_right {"foo" "bar" "baz"} {{ memo str } {
+ return "${memo}${str}"
+ }} ""] to equal "bazbarfoo"
+ }
+
+ it "has a default initial value" {
+ expect [_::reduce_right {"foo" "bar" "baz"} {{ memo str } {
+ return "${memo}${str}"
+ }}] to equal "bazbarfoo"
+ }
+
+ it "returns the initial value if given an empty list" {
+ expect [_::reduce {} {{ memo value } { }} "baz"] to equal "baz"
+ }
+
+ it "throws an error when given a list without an initial value" {
+ expect {
+ _::reduce {} {{ memo value } { }}
+ } to raise_error
+ }
+}
+
+describe "_::find" {
+ it "returns the first found value" {
+ expect [_::find {1 2 3 4} {{n} {
+ expr { $n > 2 }
+ }}] to equal 3
+ }
+
+ it "returns an empty string if no value was found" {
+ expect [_::find {1 2 3 4} {{n} {
+ return false
+ }}] to equal ""
+ }
+}
+
describe "_::times" {
it "executes the passed block n times" {
set result [list]

0 comments on commit 30f77db

Please sign in to comment.