Norvig Style

4

Click here to load reader

Transcript of Norvig Style

Page 1: Norvig Style

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 signaturesrepresentation 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 hereGlossary of impl artifactsGlossary: mapof: name of program construct -> english description. top-level-functionglobal variablesdata types -> constructors, predicates, extractorsdomain-functions - major and auxiliarylanguage built in functionslibrary 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-problemmake/measure efficiency

(6) Goto 2 for next version

(7) examine successive programs and extract tools and libraries. rewrite original programs as necessary

Page 2: Norvig Style

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 subfunctions are implemented, then implement those.

when you have 'some minimial working code', use tests to drive implementationfrom 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 codethat 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 etcfull 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 handse.g: instead of dealing for 4 players, deal for m playerse.g: instead of dealing from one deck of 52 cards, deal from p decks of whatevercollection of cards.

Page 3: Norvig Style

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, colorsassignment-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#2therefore there needs to be 'groups of properties' == say color

first approach: try assigning all combinations, test out if all predicates hold5! 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.clockfn(*args)t1 = time.clockreturn t1-t0

when you start measurements, you have transitioned from CS as mathematics toCS to experimental science

def timedcalls (n, fn,*args)times = [timedcall(fn, *args)[0]) for _ in range(n)]return min(times), average(times), max(times)

Page 4: Norvig Style

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,0result = fn(*args)print "%s got %s with %5d iters over %7d items" % (fn.__name__, result, c.starts, c.items)

use generator functions for implementing cthus

def c(sequence):c.starts += 1for item in sequence:

c.items += 1yield 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 puzzleconcept inventory build tools - here count

s,timingrefine ideas aspectsimple implementationback of envelope calcsrefine code