The practice of programming

1. Style

1.1 Names

  • Use descriptive names for globals, short name for locals. The broader the scope of a variable, the more information should be conveyed by its name.
    • method name should be based on active verbs: getTime()
    • method return boolean: isChar() inArray()
  • main concerns of programming style: descriptive names, clarity in expressions, straightforward control flow, readability of code and comments.

2. Algorithms and Data Structure

  • The behavior of some algorithms depends strongly on the input data.
  • Steps for choosing a algorithms :
    • Consider how much data the program is likely to process. Simple techniques enough? or need scalable solution?
    • Use a library or language feature
  • Data Structure: array, list, binary search tree, hash table.

Arrays support constant-time access to any element but do not grow or shrink gracefully. Lists adjust well to insertions and deletions, but take O(n) time to access random elements. Trees and hash tables provide a good compromise: rapid access to specific items combined with easy growth, so long as some balance criterion is maintained.

3. Design and Implementation

  • The design and implementation of the MarkovChain program illustrate a number of lessons for larger programs. First is the importance of choosing simple algorithms and data structures, the simplest that will do the job in reasonable time for the expected problem size.
  • It best to start detailed design with data structures, guided by knowledge of what algorithms might be used; with the data structures settled. the code goes together easily.

MarkovChainTextGenerator

package com.mtjwy.tpop.generator;

import java.io.File;
/**
 * This program is to generate random English text that reads  well.
 * The approach is to use an existing text to construct a statistical
 * model. The model presents the frequency of appearance of whole 
 * phrases used in the chosen text. From the model, this program generate new text
 * using MarkovChain Algorithm.
 * 
 * reference: The Practice of Programming
 *  
 */
public class MainApplication {
    public static final int MAX_WORDS_GENERATED = 1000;

    public static void main(String[] args) {
        MarkovChain chain = new MarkovChain();
        File file = new File("GoneWithTheWind.txt");
        chain.build(file);
        String newText = chain.generate(MAX_WORDS_GENERATED);

        System.out.println(newText);
    }

}
package com.mtjwy.tpop.generator;


import java.util.ArrayList;
import java.util.List;

/**
 * Here a Prefix is a phrase consisted of a sequence of adjacent words in a text.
 */
public class Prefix {
    private static final int MULTIPLIER = 31; // for hashCode()

    private List<String> pref;//store each word of prefix in list

    public Prefix(Prefix p) {
        //make deep copy 
        pref = new ArrayList<String>();
        for (String s : p.getPref()) {
            pref.add(s);
        }
    }

    public Prefix(int n, String str) {
        pref = new ArrayList<String>();
        for(int i = 0; i < n; i++) {
            pref.add(str);
        }
    }

    public List<String> getPref() {
        return pref;
    }

    public void setPref(List<String> pref) {
        this.pref = pref;
    }

    /*
     * Prefix object will be used as key in HashMap,
     * so need to rewrite hashCode for it. 
     */
    @Override
    public int hashCode() {
        int h = 17;
        for (String s : pref) {
            h = MULTIPLIER * h + s.hashCode();
        }
        return h;
    }

    /*
     * Need to rewrite hashCode() and equals() at the same time
     * 
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null) {
            return false;
        }
        if (getClass() != o.getClass()) {
            return false;
        }
        final Prefix p = (Prefix) o;
        for (int i = 0; i < pref.size(); i++) {
            if (!pref.get(i).equals(p.getPref().get(i))) {
                return false;
            }
        }
        return true;
    }
}
package com.mtjwy.tpop.generator;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;

public class MarkovChain {

    public static final int PREFIX_SIZE = 2;
    public static final String MARKER = "\n";

    private Prefix prefix;
    private Map<Prefix, List<String>> stateMap; // key = prefix, 
                                                // value = suffix list

    public MarkovChain() {
        prefix = new Prefix(PREFIX_SIZE, MARKER);
        stateMap = new HashMap<>();
    }

    /*
     * build the stateMap from a file
     */
    public void build(File f) {
        Scanner sc2 = null;
        try {
            sc2 = new Scanner(f);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        while (sc2.hasNextLine()) {
            Scanner s2 = new Scanner(sc2.nextLine());
            while (s2.hasNext()) {
                String s = s2.next();
                add(s);
            }
        }
        add(MARKER);
    }

    /*
     * add word to suffix list, update prefix
     */
    private void add(String word) {
        // retrieves the suffix list for the current prefix from the stateMap
        List<String> suf = stateMap.get(prefix);

        if (suf == null) {
            suf = new ArrayList<String>();
            stateMap.put(new Prefix(prefix), suf);// make a deep copy of prefix,
                                                // and use it as key
        }

        // add word to suf list
        suf.add(word);

        // advance the prefix
        prefix.getPref().remove(0);
        prefix.getPref().add(word);
    }

    /*
     * generate output string from stateMap
     */
    public String generate(int numWords) {
        prefix = stateMap.keySet().iterator().next();
        Random rand = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < numWords; i++) {
            List<String> s = stateMap.get(prefix);
            int r = rand.nextInt(s.size());
            String suf = s.get(r);
            if (suf.equals(MARKER)) {
                break;
            }
            sb.append(suf).append(" ");
            if (i != 0 && i % 8 == 0) {
                sb.append("\n");
            }
            prefix.getPref().remove(0);
            prefix.getPref().add(suf);
        }
        return sb.toString();
    }

    public Map<Prefix, List<String>> getStateMap() {
        return stateMap;
    }

    public void setStateMap(Map<Prefix, List<String>> stateMap) {
        this.stateMap = stateMap;
    }

    public Prefix getPrefix() {
        return prefix;
    }

    public void setPrefix(Prefix prefix) {
        this.prefix = prefix;
    }

}

5. Debugging

Techniques that help reduce debugging time include good design, good style, boundary condition tests, assertions and sanity checks in the code, defensive programming, well-designed interfaces, limited global data, and checking tools.

  • Multi-thread programs are not handled well by debuggers.

Debug techniques:

Good Clues, Easy Bugs

  • Think hard and add output statement and self-checking code at critical places.
  • Look for familiar patterns.
  • Examine the most recent change.
    • If you’re changing only one thing at a time as a program evolves, the bug most likely is either in the new code or has been exposed by it.
    • you should keep records of changes made and bugs fixed.
  • Don’t make the same mistake twice.
    • After you fix a bug, ask whether you might have made the same mistake somewhere else.
  • Debug it now, not later.
    • Don’t ignore a crash when it happens; track it down right away, since it may not happen again until it’s too late. A famous example occurred on the Mars Pathfinder mission.
  • Get a stack trace.
  • Read before typing.
    • read the code very carefully and think about it for a while without making changes.
  • Explain your code to someone else.

No Clues, Hard Bugs

  • Make the bug reproducible.
    • Setting the seed for random.
  • Divide and conquer.
    • Can the input that causes the program to fail be made smaller or more focused?
    • Proceed by binary search.
  • Study the numerology of failures. Sometimes a pattern in the numerology of failing examples gives a clue that focuses the search.
  • Display output to localize your search.
  • Write self-checking code.
  • Draw a picture.
  • Use tools. Compare diff of two version.
  • Keep records.

Last resorts

  • use a good debugger to step through the program.
    • wrong operator
    • scope error
    • arguments in the wrong order
    • global, shared or referenced variables are modified
    • algorithm or data structure has a fatal flaw
    • the test program is wrong
    • orgetting to close files
    • Memory “leaks”

Non-reproducible Bugs

  • Check whether all variables have been initialized

Other People’s Bugs

  • Do you have the latest version of the program?
  • Before starting, though, you must first acquire some under-standing of how the program is organized and how the original programmers thought and wrote.
  • Find cross-referencers. Track the execution flow.
  • Bug report
    • provide the owner with as good a test case as you can manage.
    • Strip the test down to a minimal and self- contained case.
    • Include other information like the version of the program itself. and of the compiler. operating system. and hardware.

6. Testing

Systematic testing, from easy tests to elaborate ones, helps ensure that programs begin life working correctly and remain correct as they grow.
Thinking about testing as you write a program will lead to better code, because that’s when you know best what the code should do.

Test as You Write the Code

  • Test code at its boundaries. It is effective to find off-by-one error.

    • condition branches go the right way
    • loop goes through the proper number of times
    • non existing or empty input
    • single input item, full array,nearly full, exactly full, and over-full
  • Test pre- and post-conditions.

  • Use assertions.
  • Program defensively.
    • A useful technique is to add code to handle “can’t happen” cases, situations where it is not logically possible for something to happen but it might anyway. (because of some failure elsewhere)
  • Check error returns.

Systematic Testing

To be orderly, keep records

  • Test incrementally.
  • Test simple parts first.
    • example of test binary search
    • 这里写图片描述
  • Know what output to expect
  • Verify conservation properties.
  • Measure test coverage.

Test Automation

  • Automate regression testing.
    • The most basic form of automation is regression testing, which performs a sequence of tests that compare the new version of something with the previous version.
    • It’s good practice to check the regression test itself periodically to make sure it is still valid.
  • Create self-contained tests.

Stress Tests

High volumes of machine-generated input are another effective testing technique.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值