from __future__ import division import types, random, heapq def accumulation(acc, f, it): """Iterate x over it, accumulating f(x) into acc, and return result.""" if isinstance(acc, (types.ClassType, types.TypeType)): a = acc() else: a = acc for x in it: if a.add(f(x), x): break return a.result() class Sum: "Accumulate the sum of a sequence of numbers" def __init__(self): self.total = 0 def add(self, exp, _): self.total += exp def result(self): return self.total class Product: "Accumulate the product of a sequence of numbers" def __init__(self): self.prod = 1 def add(self, exp, _): self.prod *= exp if exp == 0: return True def result(self): return self.prod class Max: "Accumulate the maximum value of a sequence" def __init__(self): self.seen = False def add(self, exp, _): if self.seen: self.max = max(self.max, exp) else: self.seen, self.max = True, exp def result(self): if not self.seen: raise ValueError("No values given to Max") return self.max class Min: "Accumulate the minimum value of a sequence" def __init__(self): self.seen = False def add(self, exp, _): if self.seen: self.min = min(self.min, exp) else: self.seen, self.min = True, exp def result(self): if not self.seen: raise ValueError("No values given to Min") return self.min class Argmax: "Accumulate the element of a sequence that has the maximum value" def __init__(self): self.seen = False def add(self, exp, x): if self.seen: if exp > self.max: self.max, self.x = exp, x else: self.seen, self.max, self.x = True, exp, x def result(self): if not self.seen: raise ValueError("No values given to Argmax") return self.x class Argmin: "Accumulate the element of a sequence that has the minimum value" def __init__(self): self.seen = False def add(self, exp, x): if self.seen: if exp < self.min: self.min, self.x = exp, x else: self.seen, self.min, self.x = True, exp, x def result(self): if not self.seen: raise ValueError("No values given to Argmin") return self.x class Mean: "Accumulate the average of a sequence of numbers" def __init__(self): self.total, self.n = 0, 0 def add(self, exp, _): self.total, self.n = self.total+exp, self.n+1 def result(self): return self.total / self.n class Median: """Accumulate the number that is in the middle: half above, half below. If there is an even number, try to average the middle two, or else just choose randomly from them if they can't be averaged.""" def __init__(self): self.values = [] def add(self, exp, _): self.values.append(exp) def result(self): ## There are faster algorithms, but for now just use sort values = self.values n = len(values) values.sort() if n % 2 == 1: return values[n//2] else: (m, n) = values[(n//2)-1:(n//2)+1] try: return (m + n) / 2 except TypeError: return random.choice((m, n)) class Mode: "Accumulate the most popular element of a sequence" def __init__(self): self.map = {} def add(self, exp, _): self.map[exp] = self.map.get(exp, 0) + 1 def result(self): map = self.map return accumulation(Argmax, lambda exp: map[exp], map) class Some: "An accumulator: If some exp is true, return it; otherwise return False" def __init__(self): self.value = False def add(self, exp, _): if exp: self.value = True return StopIteration def result(self): return self.value class Every: "An accumulator: If every exp is true, return True; otherwise return False" def __init__(self): self.value = True def add(self, exp, _): if not exp: self.value = False return StopIteration def result(self): return self.value class Top: def __init__(self, k): self.k = k self.heap = [] def add(self, exp, _): heap = self.heap if len(heap) < self.k: heapq.heappush(heap, exp) elif exp > heap[0]: heapq.heapreplace(heap, exp) def result(self): self.heap.sort() self.heap.reverse() return self.heap class RandomChoice: "An accumulator: choose a random item, with uniform distribution." def __init__(self): self.n, self.choice = 0, None def add(self, exp, _): self.n += 1 if random.randrange(self.n) == 0: self.choice = exp def result(self): if self.n == 0: raise ValueError, "No elements for RandomChoice" return self.choice class Join: "An accumulator: convert items to strings and join them." def __init__(self, joiner=''): self.joiner = joiner self.items = [] def add(self, exp, _): self.items.append(str(exp)) def result(self): return self.joiner.join(self.items) class SortBy: """An accumulator that sorts items using a key. [SortBy: abs(x) for x in (-2,-4,3,1)] yields (1, -2, 3, -4)]""" def __init__(self, reverse=False): self.items = [] self.reverse = reverse def add(self, exp, x): self.items.append((exp, len(self.items), x)) def result(self): self.items.sort() if self.reverse: self.items.reverse() return [x for (_, _, x) in self.items]