Re: Comments on Comments (was Re: Getting to 100 (#119))



Rick DeNatale wrote:
On 4/9/07, Phillip Gawlowski <cmdjackryan@xxxxxxxxxxxxxx> wrote:

#This does stuff to the passed argument bar
def do_stuff(bar)
#doing stuff to bar
end
^
|
Isn't superfluous, and aids debugging if the thing doesn't do what it
should. And you know that it doesn't do what it is supposed to do,
because your tests tell you that it doesn't.

Even here one needs to be careful.

As Gerry Weinberg brought out in his classic "The Psychology of
Computer Programming," comments can actually impede debugging.

If as you say the comments say what the code should be doing, there is
a tendency to debug the comments rather than the code, which can make
it harder to spot the problem. Commentary should really be left to
things which explain the context of the code. The code should indeed
speak for itself.

I guess that is what I meant. Comments should be an aid, if one comes back to the code after a few months or years, to understand what one's intentions where when writing a particular piece of code, especially if it wasn't in the "expected" or canonical way how things usually are done. And of course, the code should speak for itself, and, as I've noticed, in Ruby it usually does. When it doesn't, it is because of a lack of understanding on *my* part, not because the source code is obscure.

Weinberg even suggests a tool which strips comments from code as
useful for debugging.

In the context, that almost all comments are either vanity comments, or are of the superfluous kind (especially in statically typed languages, it is not needed to add a comment what type of variable is declared), this will be useful.

I can only speak form a hypothetical point of view, as I didn't yet use more debugging tools than ruby -w foo.rb.

But that is changing.

Now in the interest of taking my own medicine, looking at the code I
posted to rubyquiz which started all this. I see two ways where I
used comments.

1) To explain the general approach of the agorithm. It's not so much
explaining what the code should be doing as why the code is written
the way that it is.

# yield each partitioning of the receiver into count partitions
#
# This is a recursive implementation using composition of the block
# argument.
#
# The approach is to iteratively split the string into two parts,
# an initial substring of increasing length, and the remainder.
# For each initial substring we yield an array which is the concatenation
# of the initial substring and the partitioning of the remainder of
# the string into count-1 partitions.
def each_partition(count, &b)
# if the count is 1 then the only partition is the entire string.
if count == 1
yield [self]
else
# Iterate over the initial substrings, the longest initial substring must
# leave at least count-1 characters in the remaining string.
(1..(size-(count-1))).each do |initial_size|
self[initial_size..size].each_partition(count-1) {|remaining|
b.call([self[0,initial_size]] + remaining)}
end
end
end
end

That is a good example of the kind of comment I meant, as well as the second of your examples. The example above allows for a better interpretation of things, as it states the intention of the source code, as well as describing what it *should* be doing, rather than what it does. In an ideal world, both are identical, but in the real world, bugs and OS inconsistencies don't allow for the *should* and *does* to be in sync.

2) To describe an API -

# print combinations of digits and operators which evaluate to a goal
#
# Arguments are supplied by a hash the keys are:
#
# Main arguments
# :goal - the number being sought, default is 100
# :digits - a string of digits, default is "123456789"
# :ops - an array of strings representing the operators to be inserted into
# digits, default is %w[- - +]
#
# Additional arguments
# :verbose - unless false, print all attempts, default is false
# :return_counts - unless false, return an array of value, count arrays for
# values with multiple solutions, used to find interesting
# inputs, default is false
def get_to(options={})

Normally I only write comments for the second case. However in the
case of ruby-quiz submissions, I see it as an opportunity for a shared
learning experience, and a way to communicate my way of approaching
Ruby to a wide variety of folks in the audience, particularly to the
many newbies.

In this example, the comments are documentation. Great for something like RDoc, to process and output an API description, as well as making it easier if one has to look at the source code directly.

IMHO, it is only necessary to look into the source in two cases:
1) To learn.
2) To correct errors in the behavior.

--
Phillip "CynicalRyan" Gawlowski
http://cynicalryan.110mb.com/

Rule of Open-Source Programming #20:

Open Code != Good Code

.



Relevant Pages

  • Comments on Comments (was Re: Getting to 100 (#119))
    ... # yield each partitioning of the receiver into count partitions ... # an initial substring of increasing length, ... # the string into count-1 partitions. ... #:ops - an array of strings representing the operators to be inserted into ...
    (comp.lang.ruby)
  • Re: [QUIZ] Getting to 100 (#119)
    ... The main problem was partitioning the string of digits in order to ... I did this with a recursive method on String ... # an initial substring of increasing length, ... #:verbose - unless false, print all attempts, default is false ...
    (comp.lang.ruby)
  • Re: Thread exiting with code 1282 (0x502)
    ... the application exits when I display a dialog that contains text boxes with some large strings. ... I start debugging with VS2005, interact with the application so that it displays a properties dialog for one of the graphics objects we have, then I click on a button that displays another dialog with four Combo Boxes. ... 3.- I get to the Combo Box again and I enter the new long string and this time I quickly press "OK" and save the object. ... Please let me know if the implementation of OnToolTipNotify() should be different. ...
    (microsoft.public.vc.mfc)
  • Re: About VS C++
    ... I like is that every object can be simply converted to a string and therefore simply printed - big plus for debugging. ... I base most of my objects on the TtiObject class which includes handy features such as the Visitor and Iterator design pattern. ... I can also do things like MyObject.AsDebugString which then outputs the objects to a string, including a few other handy things like the ObjectState. ... I searched about such information on the FPC site, but haven't found anything about generics. ...
    (borland.public.delphi.non-technical)
  • Re: Seems to be related to the debugger..
    ... I only get garbage when I use "Start Debugging". ... Is sSum declared as wstring, ... string is stored internally, deugger basically interpets first four bytes ...
    (microsoft.public.vc.atl)