[GSOC] regular expressions beta is here

bearophile bearophileHUGS at lycos.com
Thu Aug 11 11:32:08 PDT 2011


Don:

>> 2) I have found many situations where I am able to solve a problem with both a simple and slow brute force solver, and a complex and fast algorithm to solve a problem. The little program maybe is too much slow for normal usage, but it's just few lines long (especially if I use lot of std.algorithm stuff) but it's much less likely to contain bugs.

> Sorry, but personally I don't believe that this is useful outside of toy examples.
> The question is, what bugs does it find that aren't found by a trivial unit test?

> There are two cases:
> (1) it's a very tight test. In which case, it's essentially a unit test.
> or (2) it's a very loose test. In which case, it doesn't find bugs.

Putting a simpler algorithm in the post-condition implements a third possibility you are missing.

Usually unit tests verify some specific cases (you are also able to add generic testing code in the unit test, but this is just like moving the postcondition elsewhere).

If you put an alternative algorithm in the postcondition (under debug{} if you want), you have some advantages:
- It's tight, because the second algorithm is supposed to always give the same results as the function.
- It works with the real examples the program is run too, not just the cases you have put in the unit test. Sometimes you forget to add certain cases in the unittests. Putting the test in the postcondition makes sure it always run, for all the inputs your function is run on (unless you disable it), so you will catch the cases you didn't think of in the unittests.


> The crucial feature is, they do NOTHING except find bugs in the function
> they are attached to.

In Eiffel you have the prestate too (the old), so the postcondition is the only place where such information is usable. I hope prestate will be added to D DbC, because it's a majob sub-feature of DbC. But I don't agree that postconditions are useless in D.


> For starters, it really needs to be a function with multiple return
> values. Otherwise, you can just stick asserts just before your return
> statement, and you don't need __old or any such thing.

If a function has multiple return values the out(result) helps make sure all the return paths are verified.

If the function has only one return value it helps anyway, because it helps you not forget to verify the result.


> Under what circumstances are they are more valuable than any other assert inside a function?

I have already given some answers. Another answer is this:


int foo(int x)
in {
    // ...
}
out(result) {
    auto y = computeSomething(result);
    assert(y ...);
    assert(y ...);
}
body {
    // ...
}

The out{} helps you organize your code, separating the tests of the body from the postcondition tests. Also in the postcondition you are allowed to define new variables and call things. All this out(){} code vanishes in release mode. Ho do you do that with just asserts inside the body?


If you do this the asserts will vanish in release mode, but the y will be computed still, wasting computations (a smart compiler is able to see y is not used and etc, but it's not sure this optimization happens if the computation of y is complex and it's done in-place):

int foo(int x)
in {
    // ...
}
body {
    result = ...;
    auto y = computeSomething(result);
    assert(y ...);
    assert(y ...);
    return result;
}


I presume there are ways to disable the computation of y in release mode, but I don't want to think about them. I just stick the y computation in the postcondition and the compiler will take care of it.

Bye,
bearophile


More information about the Digitalmars-d mailing list