Sei sulla pagina 1di 4

The Norvig Style of Programming

PAIP
pg 42: two alternate approaches to developing programs
(1) use most straightforward mapping of problem description to lisp code
(2) use most natural notation available to solve problem. Write interpreter for
this notation. (sa: cheat if reqd by using lisp notation as a halfway house and
reuse reader). The idea is to work with problem as much as possible.
"data driven programming"
pg: 110 five stages of programming
(1) Describe: vague understanding/english description -> 'notions'
eg: hand, hand_rank, card_rank, (GPS) problem, precondition,
appropriate action, effect
(2) Specify: notions -> datastructures,operation names,type signatures
representation of each 'notion'
eg: world_state = sets of conditions, condition = symbol,
operator = struct{action, precondition, effects},
(design decision) operator adds or deletes conditions from present state
,
In PAIP Glossary comes here
Glossary of impl artifacts
Glossary: mapof: name of program construct -> english description.
top-level-function
global variables
data types -> constructors, predicates, extractors
domain-functions - major and auxiliary
language built in functions
library functions from previous code
poker: [hands] -> hand,
card_rank: card-> number
card:(suit X value)
(3) Implement: spec -> + implementation code
Implementation code, for each artifact in glossary
(4) Test: sample data, problems, input -> program output
run on sample data. explore.
(5) Analyze: Working program -> bugs, limitations, efficiency insights
classify bugs/limitations : e.g: running-around-the-block-problem,
clobbered-sibling-goal-problem
make/measure efficiency
(6) Goto 2 for next version
(7) examine successive programs and extract tools and libraries.
rewrite original programs as necessary
e.g: interactive interpreter from GPS and Eliza
generalized pattern matching from Eliza
CS 212 class:
Step 1 as is.
2 more or less as is but very lightly touched on , since required functions are
discovered "at runtime" . still, type signatures etc are present.
glossary disappears. description added to step 2.
implementation happens Top Down , write the main function first, assuming sub
functions are implemented, then implement those.
when you have 'some minimial working code', use tests to drive implementation
from then on. (note: not compulsive TDD, though *sometimes* Test Driven is used,
though focus is still on error catching, not 'driving design')
'each part of the spec needs some code implementing in it, and a piece of code
that tests it.
That said, not too much focus on being exhaustive or using standard frameworks
-just a method in the same file with asserts. test for extreme values.
start with tests and code for top level function and work top down
use basic datastructures to represent domain concepts lists, hashes etc.
e.g: hand represented by tuple of kind, high card, other cards etc
full house eight over kings = (6, 8, 13)
flush 10 over 8 = (5, [10,8,5,7,3])
This focus on/modification of *representations* carries thru to the end
vs Class Hand etc

Use direct data structure manipulating operatons (eg: slicing lists) to get
required domain operations.
use builtins to match domain operations amap. eg: max. change (to all_max ) only
when compelled to by evolving requirements
principle of proportionate change: the change (to code) required should be
proportional to change required to concept.
One change in concept = (ideally) one change in code.
practice: keep watch for change across functions, multilines
generalize functions
(slightly? to what degree? what is the 'right level' of generalization?)
: eg: "deal" generalizes number of hands, number of cards and the deck itself!!
thus becoming highly generic.
proto thoughts = generalize any constants that occur.
e.g: instead of dealing out 5 cards, as for standard poker, deal out n sized
hands
e.g: instead of dealing for 4 players, deal for m players
e.g: instead of dealing from one deck of 52 cards, deal from p decks of whatever
collection of cards.
probability calculation of each category of hand similairly generalized!
Dimensions of programming.
imagine a multi dimensional space, with one axis each for correctness,
reliability, efficiency, features, elegance etc.
moving up only on the elegance axis, while keeping program stationary wrt other
axis == 'refactoring'
make engineering tradeoffs of one axis vs another.
DRY etc BUT here Norvig changed *REPRESENTATION* to avoid RY!!!
This careful consideration of representation
representation of hands from [7,9,7,10,7] to ((3,1,1) (7,10,9))
(and driving it towards more and more succinctness and elegance)
is characteristic of Norvig's code!!
REFLECT on changes
(eg: partition of the number 5 reflected in the rules of the game of poker)
REUSE standard pieces in *both* specification AND code.
Lesson 2:
Major focus = 'back of envelope' calculations about if a solution is
'sufficient' wrt efficiency etc
specific example = zebra puzzle
'concept inventory' = houses, properties, nationalities, pets, smokes, colors
assignment-of-properties-to-things (covers lives-in, next-to etc)
locations, next-to, left-to, right-to
assignment seems to roughly cover 'unification'
red assigned-to house#2 => blue cannot be assigned to house#2
therefore there needs to be 'groups of properties' == say color
first approach: try assigning all combinations, test out if all predicates hold
5! ways to assign colors to houses
5!^5 ways to assign five properties (color, nationality etc) to five houses
def timedcall(fn, *args)
t0 = time.clock
fn(*args)
t1 = time.clock
return t1-t0
when you start measurements, you have transitioned from CS as mathematics to
CS to experimental science
def timedcalls (n, fn,*args)
times = [timedcall(fn, *args)[0]) for _ in range(n)]
return min(times), average(times), max(times)
int-float trick
we think of different aspects of the program when we write programs.
correctness, efficiency, ease of debugging
AOP == separate out all the aspects - ideal often not reached.
def instrument_fn(fn, *args)
c.starts, c.items = 0,0
result = fn(*args)
print "%s got %s with %5d iters over %7d items" % (
fn.__name__, result, c.starts, c.items)
use generator functions for implementing c
thus
def c(sequence):
c.starts += 1
for item in sequence:
c.items += 1
yield item
key concept (also in PAIP) know how to write a profile(fn-name) function
(or as near to it as possible) which both counts number of invocations of
fn-name AND also times fn-name's fn (and reports max,min,avg,std-dev(?))
Learned from Zebra puzzle
concept inventory build tools - here count
s,timing
refine ideas aspect
simple implementation
back of envelope calcs
refine code

Potrebbero piacerti anche