771 lines
24 KiB
Python
771 lines
24 KiB
Python
# logic_utils.py
|
|
# --------------
|
|
# Licensing Information: You are free to use or extend these projects for
|
|
# educational purposes provided that (1) you do not distribute or publish
|
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
|
#
|
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
|
# The core projects and autograders were primarily created by John DeNero
|
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
|
# Student side autograding was added by Brad Miller, Nick Hay, and
|
|
# Pieter Abbeel (pabbeel@cs.berkeley.edu).
|
|
|
|
|
|
"""Provide some widely useful utilities. Safe for "from logic_utils import *".
|
|
|
|
Code originally from https://code.google.com/p/aima-python/
|
|
"""
|
|
|
|
from __future__ import generators
|
|
import operator, math, random, copy, sys, os.path, bisect, re
|
|
from functools import reduce
|
|
|
|
#______________________________________________________________________________
|
|
# Simple Data Structures: infinity, Dict, Struct
|
|
|
|
infinity = 1.0e400
|
|
|
|
def Dict(**entries):
|
|
"""Create a dict out of the argument=value arguments.
|
|
>>> Dict(a=1, b=2, c=3)
|
|
{'a': 1, 'c': 3, 'b': 2}
|
|
"""
|
|
return entries
|
|
|
|
class DefaultDict(dict):
|
|
"""Dictionary with a default value for unknown keys."""
|
|
def __init__(self, default):
|
|
self.default = default
|
|
|
|
def __getitem__(self, key):
|
|
if key in self: return self.get(key)
|
|
return self.setdefault(key, copy.deepcopy(self.default))
|
|
|
|
def __copy__(self):
|
|
copy = DefaultDict(self.default)
|
|
copy.update(self)
|
|
return copy
|
|
|
|
class Struct:
|
|
"""Create an instance with argument=value slots.
|
|
This is for making a lightweight object whose class doesn't matter."""
|
|
def __init__(self, **entries):
|
|
self.__dict__.update(entries)
|
|
|
|
def __cmp__(self, other):
|
|
if isinstance(other, Struct):
|
|
return cmp(self.__dict__, other.__dict__)
|
|
else:
|
|
return cmp(self.__dict__, other)
|
|
|
|
def __repr__(self):
|
|
args = ['%s=%s' % (k, repr(v)) for (k, v) in vars(self).items()]
|
|
return 'Struct(%s)' % ', '.join(sorted(args))
|
|
|
|
def update(x, **entries):
|
|
"""Update a dict; or an object with slots; according to entries.
|
|
>>> update({'a': 1}, a=10, b=20)
|
|
{'a': 10, 'b': 20}
|
|
>>> update(Struct(a=1), a=10, b=20)
|
|
Struct(a=10, b=20)
|
|
"""
|
|
if isinstance(x, dict):
|
|
x.update(entries)
|
|
else:
|
|
x.__dict__.update(entries)
|
|
return x
|
|
|
|
#______________________________________________________________________________
|
|
# Functions on Sequences (mostly inspired by Common Lisp)
|
|
# NOTE: Sequence functions (count_if, find_if, every, some) take function
|
|
# argument first (like reduce, filter, and map).
|
|
|
|
def removeall(item, seq):
|
|
"""Return a copy of seq (or string) with all occurences of item removed.
|
|
>>> removeall(3, [1, 2, 3, 3, 2, 1, 3])
|
|
[1, 2, 2, 1]
|
|
>>> removeall(4, [1, 2, 3])
|
|
[1, 2, 3]
|
|
"""
|
|
if isinstance(seq, str):
|
|
return seq.replace(item, '')
|
|
else:
|
|
return [x for x in seq if x != item]
|
|
|
|
def unique(seq):
|
|
"""Remove duplicate elements from seq. Assumes hashable elements.
|
|
>>> unique([1, 2, 3, 2, 1])
|
|
[1, 2, 3]
|
|
"""
|
|
return list(set(seq))
|
|
|
|
def product(numbers):
|
|
"""Return the product of the numbers.
|
|
>>> product([1,2,3,4])
|
|
24
|
|
"""
|
|
return reduce(operator.mul, numbers, 1)
|
|
|
|
def count_if(predicate, seq):
|
|
"""Count the number of elements of seq for which the predicate is true.
|
|
>>> count_if(callable, [42, None, max, min])
|
|
2
|
|
"""
|
|
f = lambda count, x: count + (not not predicate(x))
|
|
return reduce(f, seq, 0)
|
|
|
|
def find_if(predicate, seq):
|
|
"""If there is an element of seq that satisfies predicate; return it.
|
|
>>> find_if(callable, [3, min, max])
|
|
<built-in function min>
|
|
>>> find_if(callable, [1, 2, 3])
|
|
"""
|
|
for x in seq:
|
|
if predicate(x): return x
|
|
return None
|
|
|
|
def every(predicate, seq):
|
|
"""True if every element of seq satisfies predicate.
|
|
>>> every(callable, [min, max])
|
|
1
|
|
>>> every(callable, [min, 3])
|
|
0
|
|
"""
|
|
for x in seq:
|
|
if not predicate(x): return False
|
|
return True
|
|
|
|
def some(predicate, seq):
|
|
"""If some element x of seq satisfies predicate(x), return predicate(x).
|
|
>>> some(callable, [min, 3])
|
|
1
|
|
>>> some(callable, [2, 3])
|
|
0
|
|
"""
|
|
for x in seq:
|
|
px = predicate(x)
|
|
if px: return px
|
|
return False
|
|
|
|
def isin(elt, seq):
|
|
"""Like (elt in seq), but compares with is, not ==.
|
|
>>> e = []; isin(e, [1, e, 3])
|
|
True
|
|
>>> isin(e, [1, [], 3])
|
|
False
|
|
"""
|
|
for x in seq:
|
|
if elt is x: return True
|
|
return False
|
|
|
|
#______________________________________________________________________________
|
|
# Functions on sequences of numbers
|
|
# NOTE: these take the sequence argument first, like min and max,
|
|
# and like standard math notation: \sigma (i = 1..n) fn(i)
|
|
# A lot of programing is finding the best value that satisfies some condition;
|
|
# so there are three versions of argmin/argmax, depending on what you want to
|
|
# do with ties: return the first one, return them all, or pick at random.
|
|
|
|
def argmin(seq, fn):
|
|
"""Return an element with lowest fn(seq[i]) score; tie goes to first one.
|
|
>>> argmin(['one', 'to', 'three'], len)
|
|
'to'
|
|
"""
|
|
best = seq[0]; best_score = fn(best)
|
|
for x in seq:
|
|
x_score = fn(x)
|
|
if x_score < best_score:
|
|
best, best_score = x, x_score
|
|
return best
|
|
|
|
def argmin_list(seq, fn):
|
|
"""Return a list of elements of seq[i] with the lowest fn(seq[i]) scores.
|
|
>>> argmin_list(['one', 'to', 'three', 'or'], len)
|
|
['to', 'or']
|
|
"""
|
|
best_score, best = fn(seq[0]), []
|
|
for x in seq:
|
|
x_score = fn(x)
|
|
if x_score < best_score:
|
|
best, best_score = [x], x_score
|
|
elif x_score == best_score:
|
|
best.append(x)
|
|
return best
|
|
|
|
def argmin_random_tie(seq, fn):
|
|
"""Return an element with lowest fn(seq[i]) score; break ties at random.
|
|
Thus, for all s,f: argmin_random_tie(s, f) in argmin_list(s, f)"""
|
|
best_score = fn(seq[0]); n = 0
|
|
for x in seq:
|
|
x_score = fn(x)
|
|
if x_score < best_score:
|
|
best, best_score = x, x_score; n = 1
|
|
elif x_score == best_score:
|
|
n += 1
|
|
if random.randrange(n) == 0:
|
|
best = x
|
|
return best
|
|
|
|
def argmax(seq, fn):
|
|
"""Return an element with highest fn(seq[i]) score; tie goes to first one.
|
|
>>> argmax(['one', 'to', 'three'], len)
|
|
'three'
|
|
"""
|
|
return argmin(seq, lambda x: -fn(x))
|
|
|
|
def argmax_list(seq, fn):
|
|
"""Return a list of elements of seq[i] with the highest fn(seq[i]) scores.
|
|
>>> argmax_list(['one', 'three', 'seven'], len)
|
|
['three', 'seven']
|
|
"""
|
|
return argmin_list(seq, lambda x: -fn(x))
|
|
|
|
def argmax_random_tie(seq, fn):
|
|
"Return an element with highest fn(seq[i]) score; break ties at random."
|
|
return argmin_random_tie(seq, lambda x: -fn(x))
|
|
#______________________________________________________________________________
|
|
# Statistical and mathematical functions
|
|
|
|
def histogram(values, mode=0, bin_function=None):
|
|
"""Return a list of (value, count) pairs, summarizing the input values.
|
|
Sorted by increasing value, or if mode=1, by decreasing count.
|
|
If bin_function is given, map it over values first."""
|
|
if bin_function: values = map(bin_function, values)
|
|
bins = {}
|
|
for val in values:
|
|
bins[val] = bins.get(val, 0) + 1
|
|
if mode:
|
|
return sorted(bins.items(), key=lambda x: (x[1],x[0]), reverse=True)
|
|
else:
|
|
return sorted(bins.items())
|
|
|
|
def log2(x):
|
|
"""Base 2 logarithm.
|
|
>>> log2(1024)
|
|
10.0
|
|
"""
|
|
return math.log10(x) / math.log10(2)
|
|
|
|
def mode(values):
|
|
"""Return the most common value in the list of values.
|
|
>>> mode([1, 2, 3, 2])
|
|
2
|
|
"""
|
|
return histogram(values, mode=1)[0][0]
|
|
|
|
def median(values):
|
|
"""Return the middle value, when the values are sorted.
|
|
If there are an odd number of elements, try to average the middle two.
|
|
If they can't be averaged (e.g. they are strings), choose one at random.
|
|
>>> median([10, 100, 11])
|
|
11
|
|
>>> median([1, 2, 3, 4])
|
|
2.5
|
|
"""
|
|
n = len(values)
|
|
values = sorted(values)
|
|
if n % 2 == 1:
|
|
return values[n/2]
|
|
else:
|
|
middle2 = values[(n/2)-1:(n/2)+1]
|
|
try:
|
|
return mean(middle2)
|
|
except TypeError:
|
|
return random.choice(middle2)
|
|
|
|
def mean(values):
|
|
"""Return the arithmetic average of the values."""
|
|
return sum(values) / float(len(values))
|
|
|
|
def stddev(values, meanval=None):
|
|
"""The standard deviation of a set of values.
|
|
Pass in the mean if you already know it."""
|
|
if meanval is None: meanval = mean(values)
|
|
return math.sqrt(sum([(x - meanval)**2 for x in values]) / (len(values)-1))
|
|
|
|
def dotproduct(X, Y):
|
|
"""Return the sum of the element-wise product of vectors x and y.
|
|
>>> dotproduct([1, 2, 3], [1000, 100, 10])
|
|
1230
|
|
"""
|
|
return sum([x * y for x, y in zip(X, Y)])
|
|
|
|
def vector_add(a, b):
|
|
"""Component-wise addition of two vectors.
|
|
>>> vector_add((0, 1), (8, 9))
|
|
(8, 10)
|
|
"""
|
|
return tuple(map(operator.add, a, b))
|
|
|
|
def probability(p):
|
|
"Return true with probability p."
|
|
return p > random.uniform(0.0, 1.0)
|
|
|
|
def weighted_sample_with_replacement(seq, weights, n):
|
|
"""Pick n samples from seq at random, with replacement, with the
|
|
probability of each element in proportion to its corresponding
|
|
weight."""
|
|
sample = weighted_sampler(seq, weights)
|
|
return [sample() for s in range(n)]
|
|
|
|
def weighted_sampler(seq, weights):
|
|
"Return a random-sample function that picks from seq weighted by weights."
|
|
totals = []
|
|
for w in weights:
|
|
totals.append(w + totals[-1] if totals else w)
|
|
return lambda: seq[bisect.bisect(totals, random.uniform(0, totals[-1]))]
|
|
|
|
def num_or_str(x):
|
|
"""The argument is a string; convert to a number if possible, or strip it.
|
|
>>> num_or_str('42')
|
|
42
|
|
>>> num_or_str(' 42x ')
|
|
'42x'
|
|
"""
|
|
if isnumber(x): return x
|
|
try:
|
|
return int(x)
|
|
except ValueError:
|
|
try:
|
|
return float(x)
|
|
except ValueError:
|
|
return str(x).strip()
|
|
|
|
def normalize(numbers):
|
|
"""Multiply each number by a constant such that the sum is 1.0
|
|
>>> normalize([1,2,1])
|
|
[0.25, 0.5, 0.25]
|
|
"""
|
|
total = float(sum(numbers))
|
|
return [n / total for n in numbers]
|
|
|
|
def clip(x, lowest, highest):
|
|
"""Return x clipped to the range [lowest..highest].
|
|
>>> [clip(x, 0, 1) for x in [-1, 0.5, 10]]
|
|
[0, 0.5, 1]
|
|
"""
|
|
return max(lowest, min(x, highest))
|
|
|
|
#______________________________________________________________________________
|
|
## OK, the following are not as widely useful utilities as some of the other
|
|
## functions here, but they do show up wherever we have 2D grids: Wumpus and
|
|
## Vacuum worlds, TicTacToe and Checkers, and markov decision Processes.
|
|
|
|
orientations = [(1, 0), (0, 1), (-1, 0), (0, -1)]
|
|
|
|
def turn_heading(heading, inc, headings=orientations):
|
|
return headings[(headings.index(heading) + inc) % len(headings)]
|
|
|
|
def turn_right(heading):
|
|
return turn_heading(heading, -1)
|
|
|
|
def turn_left(heading):
|
|
return turn_heading(heading, +1)
|
|
|
|
def distance(a, b):
|
|
"The distance between two (x, y) points."
|
|
(ax, ay) = a
|
|
(bx, by) = b
|
|
return math.hypot((ax - bx), (ay - by))
|
|
|
|
def distance2(a, b):
|
|
"The square of the distance between two (x, y) points."
|
|
(ax, ay) = a
|
|
(bx, by) = b
|
|
return (ax - bx)**2 + (ay - by)**2
|
|
|
|
def vector_clip(vector, lowest, highest):
|
|
"""Return vector, except if any element is less than the corresponding
|
|
value of lowest or more than the corresponding value of highest, clip to
|
|
those values.
|
|
>>> vector_clip((-1, 10), (0, 0), (9, 9))
|
|
(0, 9)
|
|
"""
|
|
return type(vector)(map(clip, vector, lowest, highest))
|
|
|
|
#______________________________________________________________________________
|
|
# Misc Functions
|
|
|
|
def printf(format, *args):
|
|
"""Format args with the first argument as format string, and write.
|
|
Return the last arg, or format itself if there are no args."""
|
|
sys.stdout.write(str(format) % args)
|
|
return if_(args, lambda: args[-1], lambda: format)
|
|
|
|
def caller(n=1):
|
|
"""Return the name of the calling function n levels up in the frame stack.
|
|
>>> caller(0)
|
|
'caller'
|
|
>>> def f():
|
|
... return caller()
|
|
>>> f()
|
|
'f'
|
|
"""
|
|
import inspect
|
|
return inspect.getouterframes(inspect.currentframe())[n][3]
|
|
|
|
def memoize(fn, slot=None):
|
|
"""Memoize fn: make it remember the computed value for any argument list.
|
|
If slot is specified, store result in that slot of first argument.
|
|
If slot is false, store results in a dictionary."""
|
|
if slot:
|
|
def memoized_fn(obj, *args):
|
|
if hasattr(obj, slot):
|
|
return getattr(obj, slot)
|
|
else:
|
|
val = fn(obj, *args)
|
|
setattr(obj, slot, val)
|
|
return val
|
|
else:
|
|
def memoized_fn(*args):
|
|
if not memoized_fn.cache.has_key(args):
|
|
memoized_fn.cache[args] = fn(*args)
|
|
return memoized_fn.cache[args]
|
|
memoized_fn.cache = {}
|
|
return memoized_fn
|
|
|
|
def if_(test, result, alternative):
|
|
"""Like C++ and Java's (test ? result : alternative), except
|
|
both result and alternative are always evaluated. However, if
|
|
either evaluates to a function, it is applied to the empty arglist,
|
|
so you can delay execution by putting it in a lambda.
|
|
>>> if_(2 + 2 == 4, 'ok', lambda: expensive_computation())
|
|
'ok'
|
|
"""
|
|
if test:
|
|
if callable(result): return result()
|
|
return result
|
|
else:
|
|
if callable(alternative): return alternative()
|
|
return alternative
|
|
|
|
def name(object):
|
|
"Try to find some reasonable name for the object."
|
|
return (getattr(object, 'name', 0) or getattr(object, '__name__', 0)
|
|
or getattr(getattr(object, '__class__', 0), '__name__', 0)
|
|
or str(object))
|
|
|
|
def isnumber(x):
|
|
"Is x a number? We say it is if it has a __int__ method."
|
|
return hasattr(x, '__int__')
|
|
|
|
def issequence(x):
|
|
"Is x a sequence? We say it is if it has a __getitem__ method."
|
|
return hasattr(x, '__getitem__')
|
|
|
|
def print_table(table, header=None, sep=' ', numfmt='%g'):
|
|
"""Print a list of lists as a table, so that columns line up nicely.
|
|
header, if specified, will be printed as the first row.
|
|
numfmt is the format for all numbers; you might want e.g. '%6.2f'.
|
|
(If you want different formats in different columns, don't use print_table.)
|
|
sep is the separator between columns."""
|
|
justs = [if_(isnumber(x), 'rjust', 'ljust') for x in table[0]]
|
|
if header:
|
|
table = [header] + table
|
|
table = [[if_(isnumber(x), lambda: numfmt % x, lambda: x) for x in row]
|
|
for row in table]
|
|
maxlen = lambda seq: max(map(len, seq))
|
|
sizes = map(maxlen, zip(*[map(str, row) for row in table]))
|
|
for row in table:
|
|
print(sep.join(getattr(str(x), j)(size)
|
|
for (j, size, x) in zip(justs, sizes, row)))
|
|
|
|
def AIMAFile(components, mode='r'):
|
|
"Open a file based at the AIMA root directory."
|
|
import logic_utils
|
|
dir = os.path.dirname(logic_utils.__file__)
|
|
return open(apply(os.path.join, [dir] + components), mode)
|
|
|
|
def DataFile(name, mode='r'):
|
|
"Return a file in the AIMA /data directory."
|
|
return AIMAFile(['..', 'data', name], mode)
|
|
|
|
def unimplemented():
|
|
"Use this as a stub for not-yet-implemented functions."
|
|
raise NotImplementedError()
|
|
|
|
#______________________________________________________________________________
|
|
# Queues: Stack, FIFOQueue, PriorityQueue
|
|
|
|
class Queue:
|
|
"""Queue is an abstract class/interface. There are three types:
|
|
Stack(): A Last In First Out Queue.
|
|
FIFOQueue(): A First In First Out Queue.
|
|
PriorityQueue(order, f): Queue in sorted order (default min-first).
|
|
Each type supports the following methods and functions:
|
|
q.append(item) -- add an item to the queue
|
|
q.extend(items) -- equivalent to: for item in items: q.append(item)
|
|
q.pop() -- return the top item from the queue
|
|
len(q) -- number of items in q (also q.__len())
|
|
item in q -- does q contain item?
|
|
Note that isinstance(Stack(), Queue) is false, because we implement stacks
|
|
as lists. If Python ever gets interfaces, Queue will be an interface."""
|
|
|
|
def __init__(self):
|
|
abstract
|
|
|
|
def extend(self, items):
|
|
for item in items: self.append(item)
|
|
|
|
def Stack():
|
|
"""Return an empty list, suitable as a Last-In-First-Out Queue."""
|
|
return []
|
|
|
|
class FIFOQueue(Queue):
|
|
"""A First-In-First-Out Queue."""
|
|
def __init__(self):
|
|
self.A = []; self.start = 0
|
|
def append(self, item):
|
|
self.A.append(item)
|
|
def __len__(self):
|
|
return len(self.A) - self.start
|
|
def extend(self, items):
|
|
self.A.extend(items)
|
|
def pop(self):
|
|
e = self.A[self.start]
|
|
self.start += 1
|
|
if self.start > 5 and self.start > len(self.A)/2:
|
|
self.A = self.A[self.start:]
|
|
self.start = 0
|
|
return e
|
|
def __contains__(self, item):
|
|
return item in self.A[self.start:]
|
|
|
|
class PriorityQueue(Queue):
|
|
"""A queue in which the minimum (or maximum) element (as determined by f and
|
|
order) is returned first. If order is min, the item with minimum f(x) is
|
|
returned first; if order is max, then it is the item with maximum f(x).
|
|
Also supports dict-like lookup."""
|
|
def __init__(self, order=min, f=lambda x: x):
|
|
update(self, A=[], order=order, f=f)
|
|
def append(self, item):
|
|
bisect.insort(self.A, (self.f(item), item))
|
|
def __len__(self):
|
|
return len(self.A)
|
|
def pop(self):
|
|
if self.order == min:
|
|
return self.A.pop(0)[1]
|
|
else:
|
|
return self.A.pop()[1]
|
|
def __contains__(self, item):
|
|
return some(lambda _, x: x == item, self.A)
|
|
def __getitem__(self, key):
|
|
for _, item in self.A:
|
|
if item == key:
|
|
return item
|
|
def __delitem__(self, key):
|
|
for i, (value, item) in enumerate(self.A):
|
|
if item == key:
|
|
self.A.pop(i)
|
|
return
|
|
|
|
## Fig: The idea is we can define things like Fig[3,10] later.
|
|
## Alas, it is Fig[3,10] not Fig[3.10], because that would be the same
|
|
## as Fig[3.1]
|
|
Fig = {}
|
|
|
|
#______________________________________________________________________________
|
|
# Support for doctest
|
|
|
|
def ignore(x): None
|
|
|
|
def random_tests(text):
|
|
"""Some functions are stochastic. We want to be able to write a test
|
|
with random output. We do that by ignoring the output."""
|
|
def fixup(test):
|
|
if " = " in test:
|
|
return ">>> " + test
|
|
else:
|
|
return ">>> ignore(" + test + ")"
|
|
tests = re.findall(">>> (.*)", text)
|
|
return '\n'.join(map(fixup, tests))
|
|
|
|
#______________________________________________________________________________
|
|
|
|
__doc__ += """
|
|
>>> d = DefaultDict(0)
|
|
>>> d['x'] += 1
|
|
>>> d['x']
|
|
1
|
|
|
|
>>> d = DefaultDict([])
|
|
>>> d['x'] += [1]
|
|
>>> d['y'] += [2]
|
|
>>> d['x']
|
|
[1]
|
|
|
|
>>> s = Struct(a=1, b=2)
|
|
>>> s.a
|
|
1
|
|
>>> s.a = 3
|
|
>>> s
|
|
Struct(a=3, b=2)
|
|
|
|
>>> def is_even(x):
|
|
... return x % 2 == 0
|
|
>>> sorted([1, 2, -3])
|
|
[-3, 1, 2]
|
|
>>> sorted(range(10), key=is_even)
|
|
[1, 3, 5, 7, 9, 0, 2, 4, 6, 8]
|
|
>>> sorted(range(10), lambda x,y: y-x)
|
|
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
|
|
|
>>> removeall(4, [])
|
|
[]
|
|
>>> removeall('s', 'This is a test. Was a test.')
|
|
'Thi i a tet. Wa a tet.'
|
|
>>> removeall('s', 'Something')
|
|
'Something'
|
|
>>> removeall('s', '')
|
|
''
|
|
|
|
>>> list(reversed([]))
|
|
[]
|
|
|
|
>>> count_if(is_even, [1, 2, 3, 4])
|
|
2
|
|
>>> count_if(is_even, [])
|
|
0
|
|
|
|
>>> argmax([1], lambda x: x*x)
|
|
1
|
|
>>> argmin([1], lambda x: x*x)
|
|
1
|
|
|
|
|
|
# Test of memoize with slots in structures
|
|
>>> countries = [Struct(name='united states'), Struct(name='canada')]
|
|
|
|
# Pretend that 'gnp' was some big hairy operation:
|
|
>>> def gnp(country):
|
|
... print('calculating gnp ...')
|
|
... return len(country.name) * 1e10
|
|
|
|
>>> gnp = memoize(gnp, '_gnp')
|
|
>>> list(map(gnp, countries))
|
|
calculating gnp ...
|
|
calculating gnp ...
|
|
[130000000000.0, 60000000000.0]
|
|
>>> countries
|
|
[Struct(_gnp=130000000000.0, name='united states'), Struct(_gnp=60000000000.0, name='canada')]
|
|
|
|
# This time we avoid re-doing the calculation
|
|
>>> list(map(gnp, countries))
|
|
[130000000000.0, 60000000000.0]
|
|
|
|
# Test Queues:
|
|
>>> nums = [1, 8, 2, 7, 5, 6, -99, 99, 4, 3, 0]
|
|
>>> def qtest(q):
|
|
... q.extend(nums)
|
|
... for num in nums: assert num in q
|
|
... assert 42 not in q
|
|
... return [q.pop() for i in range(len(q))]
|
|
>>> qtest(Stack())
|
|
[0, 3, 4, 99, -99, 6, 5, 7, 2, 8, 1]
|
|
|
|
>>> qtest(FIFOQueue())
|
|
[1, 8, 2, 7, 5, 6, -99, 99, 4, 3, 0]
|
|
|
|
>>> qtest(PriorityQueue(min))
|
|
[-99, 0, 1, 2, 3, 4, 5, 6, 7, 8, 99]
|
|
|
|
>>> qtest(PriorityQueue(max))
|
|
[99, 8, 7, 6, 5, 4, 3, 2, 1, 0, -99]
|
|
|
|
>>> qtest(PriorityQueue(min, abs))
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, -99, 99]
|
|
|
|
>>> qtest(PriorityQueue(max, abs))
|
|
[99, -99, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
|
|
|
>>> vals = [100, 110, 160, 200, 160, 110, 200, 200, 220]
|
|
>>> histogram(vals)
|
|
[(100, 1), (110, 2), (160, 2), (200, 3), (220, 1)]
|
|
>>> histogram(vals, 1)
|
|
[(200, 3), (160, 2), (110, 2), (220, 1), (100, 1)]
|
|
>>> histogram(vals, 1, lambda v: round(v, -2))
|
|
[(200.0, 6), (100.0, 3)]
|
|
|
|
>>> log2(1.0)
|
|
0.0
|
|
|
|
>>> def fib(n):
|
|
... return (n<=1 and 1) or (fib(n-1) + fib(n-2))
|
|
|
|
>>> fib(9)
|
|
55
|
|
|
|
# Now we make it faster:
|
|
>>> fib = memoize(fib)
|
|
>>> fib(9)
|
|
55
|
|
|
|
>>> q = Stack()
|
|
>>> q.append(1)
|
|
>>> q.append(2)
|
|
>>> q.pop(), q.pop()
|
|
(2, 1)
|
|
|
|
>>> q = FIFOQueue()
|
|
>>> q.append(1)
|
|
>>> q.append(2)
|
|
>>> q.pop(), q.pop()
|
|
(1, 2)
|
|
|
|
|
|
>>> abc = set('abc')
|
|
>>> bcd = set('bcd')
|
|
>>> 'a' in abc
|
|
True
|
|
>>> 'a' in bcd
|
|
False
|
|
>>> list(abc.intersection(bcd))
|
|
['c', 'b']
|
|
>>> list(abc.union(bcd))
|
|
['a', 'c', 'b', 'd']
|
|
|
|
## From "What's new in Python 2.4", but I added calls to sl
|
|
|
|
>>> def sl(x):
|
|
... return sorted(list(x))
|
|
|
|
|
|
>>> a = set('abracadabra') # form a set from a string
|
|
>>> 'z' in a # fast membership testing
|
|
False
|
|
>>> sl(a) # unique letters in a
|
|
['a', 'b', 'c', 'd', 'r']
|
|
|
|
>>> b = set('alacazam') # form a second set
|
|
>>> sl(a - b) # letters in a but not in b
|
|
['b', 'd', 'r']
|
|
>>> sl(a | b) # letters in either a or b
|
|
['a', 'b', 'c', 'd', 'l', 'm', 'r', 'z']
|
|
>>> sl(a & b) # letters in both a and b
|
|
['a', 'c']
|
|
>>> sl(a ^ b) # letters in a or b but not both
|
|
['b', 'd', 'l', 'm', 'r', 'z']
|
|
|
|
|
|
>>> a.add('z') # add a new element
|
|
>>> a.update('wxy') # add multiple new elements
|
|
>>> sl(a)
|
|
['a', 'b', 'c', 'd', 'r', 'w', 'x', 'y', 'z']
|
|
>>> a.remove('x') # take one element out
|
|
>>> sl(a)
|
|
['a', 'b', 'c', 'd', 'r', 'w', 'y', 'z']
|
|
|
|
>>> weighted_sample_with_replacement([], [], 0)
|
|
[]
|
|
>>> weighted_sample_with_replacement('a', [3], 2)
|
|
['a', 'a']
|
|
>>> weighted_sample_with_replacement('ab', [0, 3], 3)
|
|
['b', 'b', 'b']
|
|
"""
|
|
|
|
__doc__ += random_tests("""
|
|
>>> weighted_sample_with_replacement(range(10), [x*x for x in range(10)], 3)
|
|
[8, 9, 6]
|
|
""")
|