# predicates - All, Any predicates and vector operations on iterables # (c) 2002-2003 Christos Georgiou - TZOTZIOY tzot@sil-tec.gr # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation, # version 2.1 of the License. # See http://www.gnu.org/copyleft/lesser.txt for the full text. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. def definer(): # a dummy function to turn name lookups into local ones import operator from itertools import repeat, imap, ifilterfalse, ifilter, izip, starmap from new import instancemethod, function as new_function # voodoo stuff from __builtin__ import True, False, None as NONE, iter, apply, object, str operator_functions= set(dir(operator)) # the voodoo function makes allows heave use of currying in C # through the def voodoo(function, *its_arguments): def child(function, first_argument, *rest_of_arguments): if rest_of_arguments: return child( instancemethod(function, first_argument, object), *rest_of_arguments ) else: return instancemethod(function, first_argument, object) return child(function, *its_arguments) alltrue= voodoo(ifilter, operator.truth) allfalse= voodoo(ifilterfalse, operator.truth) class All(object): """Array operations on every element of its argument.""" __slots__= "_iterable", def __init__(self, iterable): self._iterable= iterable def __nonzero__(self): """Return nonzero if all results are nonzero.""" return False not in allfalse(self._iterable) def __iter__(self): """Iterate through self._iterable .""" return iter(self._iterable) def __call__(self, *args, **kwargs): self._iterable= starmap( apply, izip( self._iterable, repeat(args), repeat(kwargs), ) # izip ) # starmap return self def do(self, function, *values): if values: return self.__class__( imap(function, self._iterable, repeat(*values))) else: return self.__class__( imap(function, self._iterable)) def __getattr__(self, attribute): """Return a generator iterating over every item's attribute.""" self._iterable= imap(operator.attrgetter(attribute), self._iterable) return self def __getitem__(self, item): self._iterable= imap(operator.itemgetter(item), self._iterable) return self def __str__(self): return str(list(self)) # now that the definition of All is over, we will add methods # that can be implemented using the operator module (__add__, __sub__ etc) # at first, there is the function_factory def function_factory(method_name, function_name, argument_count, reverse_name): operator_method= getattr(operator, function_name) if reverse_name: reverse_method= getattr(operator, reverse_name) if argument_count==1: def operate(self): ## print "called single", operator_method self._iterable= imap(operator_method, self._iterable) return self elif argument_count==2: if reverse_name: def operate(self, other): ## print "called dual clever", operator_method, other self._iterable= imap( voodoo(reverse_method, other), self._iterable ) return self else: def operate(self, other): ## print "called dual", operator_method, other self._iterable= imap(operator_method, self._iterable, repeat(other)) return self elif argument_count==3: def operate(self, other, another): self._iterable=\ imap(operator_method, self._iterable, repeat(other), repeat(another)) return self return operate operator_methods= ( (1, "abs neg pos inv invert not "), (2, "lt/ge le/gt eq/eq ne/ne ge/lt gt/le add/add and/and div floordiv" " lshift mod mul/mul or/or pow" " radd=add/add rsub=sub/sub rmul=mul/mul rand=and/and rdiv=div/div" " ror=or/or rfloordiv=floordiv/floordiv rtruediv=truediv/truediv" " rshift sub truediv xor/xor concat contains repeat"), (3, "getslice"), ) global_variables= globals() for argument_count, function_names in operator_methods: for function_name in imap("__%s__".__mod__, function_names.split()): if "/" in function_name: function_name, reverse_name= function_name.replace("/", "__/__").split("/") else: reverse_name='' if "=" in function_name: method_name, function_name= function_name.replace("=", "__=__").split("=") else: method_name= function_name operation= function_factory( method_name, function_name, argument_count, reverse_name) setattr( All, method_name, instancemethod( new_function( operation.func_code, global_variables, method_name, operation.func_defaults, operation.func_closure ), NONE, All ) ) ## ## for function_name in operator_functions: ## if function_name.startswith("__") and function_name not in ("__doc__", "__name__",): ## print function_name ## setattr(All, function_name, function_factory(function_name)) ## print repr(All.do) ## print repr(All.__contains__) class Any(All): __slots__= () def __nonzero__(self): """Return nonzero if any result is non zero.""" return True in alltrue(self._iterable) return dict( All=All, Any=Any, ) globals().update(definer()); del definer if __name__=="__main__": li= [0, 2, 4, 6, 8] ls= ["hello", "hell", "help"] print "Integer list:", li print "String list:", ls # do math operations on all items print "Add 5 to integer list:", list(All(li)+5) if All(ls)[1:3]=="el": print "All strings contain 'el' at position 1." else: print "[ERROR] Some strings do not contain 'el' at position 1!!!" if All(ls)[1:4]=="elp": print "[ERROR] All strings contain 'elp' at position 1!!!" print All(ls)[1:4] == "elp" else: print "Some strings do not contain 'elp' at position 1." if All(li)%2 == 0: print "All integers are even." else: print "[ERROR] Some integers are not even!!!" print All(li)%2 if "el" in All(ls): print "All strings contain 'el'." else: print "[ERROR] Some strings do not contain 'el'!!!" if Any(li)%3 == 0: print "Some integers are multiples of 3." else: print "[ERROR] All integers are not multiples of 3!!!" if "elp" in Any(ls): print "Some strings contain 'elp'." else: print "[ERROR] No strings contain 'elp'!!!" # call a method on all items if All(ls).startswith("hel"): print "All strings start with 'hel'" else: print "[ERROR] Some strings do not start with 'hel'!!!" # benchmarks li= xrange(100000) print "adding 45 to 100000 numbers" print max(45+All(li)) print "max above" print "reverse the above operation" print max(All(li)+45) print "was it slower?" print "100/All([1,2,3])" print list(100/All([1,2,3])) import time ## import hotshot, hotshot.stats ## prf=hotshot.Profile("c:/temp/profile", 1) def simplecheck(alist, number): mitsos= 0 for index, item in enumerate(alist): mitsos+= (item+13)%number def complexcheck(alist, number): for item in alist: if item*2-8>number: return False return True def testfunction(function, alist, number): t0= time.clock() for ix in xrange(100): if function(alist, number): pass return time.clock()-t0 def test1(): li=range(12000) print "simplecheck 1500", testfunction(simplecheck, li, 300), "sec" print "simplecheck 10000", testfunction(simplecheck, li, 2000), "sec" li=range(12000) print "complexcheck 1500", testfunction(complexcheck, li, 300), "sec" print "complexcheck 10000", testfunction(complexcheck, li, 2000), "sec" ## prf.runcall(test1) test1() def testfunctionsimple(alist, number): t0= time.clock() for ix in xrange(100): mitsos= sum((All(alist)+13)%number) return time.clock()-t0 def testfunctioncomplex(alist, number): t0= time.clock() for ix in xrange(100): if -8+Any(alist)*2>number: pass return time.clock()-t0 def test2(): li= range(12000) print "simpleAll 1500", testfunctionsimple(li, 300), "sec" print "simpleAll 10000", testfunctionsimple(li, 2000), "sec" li= range(12000) print "complexAll 1500", testfunctioncomplex(li, 300), "sec" print "complexAll 10000", testfunctioncomplex(li, 2000), "sec" test2() ## prf.runcall(test2) ## prf.close() ## stats= hotshot.stats.load("c:/temp/profile") ## stats.strip_dirs() ## stats.sort_stats('time', 'calls') ## stats.print_stats(40) ## import math ## functions= math.sin, math.cos, math.atan ## numbers= [math.radians(x) for x in range(0, 90, 5)] ## print "Tricky:" ## print All(numbers).do(All(functions))