Version 10 of Filter in functional programming

Updated 2007-05-01 07:22:03 by suchenwi

SS 29Oct2004: filter is a very useful function in functional programming, that given a list and a predicate (a function with arity of one, returing true or false) creates a new list which contains only the members of the original list meeting the predicate (in the same order they appeared).

I plan to submit a TIP for the addition of this function together with map in functional programming.

The following is an implementation for Tcl, with this command structure:

 filter var list expr

Some usage example:

 % filter x {1 2 3 4 5 6 7 8 9} {$x > 4}
 5 6 7 8 9
 % filter x {1 2 3 4 5 6 7 8 9} {$x % 3}
 1 2 4 5 7 8
 % filter x {1 2 3 4 5 6 7 8 9} {($x % 3) == 0}
 3 6 9
 % filter x [list a b {} c d e {} {} f g] {[llength $x]}
 a b c d e f g

Do you need filter? If you can see the following pattern in your code, you need it:

 set res {}
 foreach x $list {
    if {[foobar $x]} {
        lappend res $x
    }
 }
 use $res

Finally the (very simple) code:

 proc filter {fvar flist fexpr} {
     upvar 1 $fvar var
     set res {}
     foreach var $flist {
         set varCopy $var
         if {[uplevel 1 [list expr $fexpr]]} {
             lappend res $varCopy
         }
     }
     return $res
 }

RS would prefer an argument sequence with the list in the end:

 filter x {$x > 4} $list

so the second and third argument, which form a lambda, are left together.


SS Indeed, it's not clear what's the best. I like more what you proposed from an aesthetic point of view, but to have the same arguments order as map and foreach can be a good point.

There is another option too:

 filter $list x {$x > 4}

that sounds to me slightly better.


Duoas writes his version

 proc lfilter {ls varName script}
     upvar 1 $varName var
     set result {}
     foreach var $ls val $ls {
         if {[uplevel 1 eval $script]} {
             lappend result $val
             }
         }
     return $result
     }

and his motive (no expr)

 % set mixed {1 abc 2.5 twelve Annie -3 0}
 % lfilter $mixed x {string is double -strict $x}
 1 2.5 -3 0

Hope this helps.

AM (21 june 2006) But that would fail in circumstances where a numeric conditions is needed - "$x > 4" would need to become "expr {$x > 4}", not a pleasant user requirement, IMO


True, but I'm still not so sure I'm convinced either way. I don't like being constrained. In any case, with the advent of apply in Tcl 8.5, we can use true lambdas instead of the versions we've seen so far:

  proc ::filter {predicate ls} {
    set result {}
    foreach el $ls {
      if {[eval apply [list $predicate] $el]} {
        lappend result $el
        }
      }
    return $result
    }

This version is particularly capable because you can choose between lambdas taking each element in the list as a whole or as individual arguments:

  % filter {{a b} {expr {$a == $b}}} {{2 2} {3 -7} {42 9} {7 7}}
  {2 2} {7 7}

  % filter {args {expr {[llength $args] == 2}}} {one {two words} only one {three words here} {two again}}
  {two words} {two again}

01-05-2007 Duoas


RS Again, here a version that uses {*}:

 proc ::filter {predicate ls} {
    set result {}
    foreach el $ls {
       if [apply $predicate {*}$el] {lappend result $el}
    }
    return $result
 }

Category Algorithm|Category Functional Programming