A few years ago, I was working on a project where our client asked for code review by experts from another company. The idea behind this was to assess that we were doing a good job, fair enough. Since they didn’t have any in-house experts on the matter, the only choice left was to hire experts on the topic to review our work.
I don’t remember all the details of this code review, but I have two remarks that distinctly remember because they shocked me.
No Need to Have a Static Private Object to Lock On, Just Lock the Type
Now when I was told that, I was hit very hard because I was certain that the language specifications specifically mentioned that as a bad practice. Turns out that I remembered well: http://msdn.microsoft.com/en-us/library/c5kehkcz.
The good thing is that this one, I managed to avoid having to change it in the code. I don’t remember very well the why, but it was a small victory.
Don’t Use the “+” Operator on String, Use String.Concat
This one was also a good one. When I was told that I lost all hopes that these experts would be useful, as this comment revealed their ignorance of the C# compiler behavior. Not that I think everyone should know that, but I don’t consider myself as a C# guru, but I believe that any enthusiast that played with reflector a little bit should have noticed that.
Not only the “+” operator is replaced by String.Concat call if the content of both operand is not known at compile time, but if both are known at compile time then it is simply replaced by one single String! So, applying this guideline did reduce performances… Not to a noticeable way, I agree, but having to go in your code and replace good stuff with something not as good is not a pleasant experience.
The “fun” part is that actually we also had the String.Concat comment for the “+=” operator. So, one of my colleague went through the code and replaced all the
s1 += s2;
which is wrong since the return value is ignored, so original string is not modified. Not only this was a pointless change, but bugs were introduced when doing that mindless task.
I don’t know if there is anything to be concluded from this experience. In my case, the advices provided were mostly useless if not harmful. My personal conclusion is that real experts are a rare breed, and it’s not because someone labeled as an expert and expensive that he is. Even if he comes from a big corporation…
Now there is also a conflict of interest here. When an expert is dispatched on this kind of mission, it would be very difficult to justify his price if he didn’t have anything to comment on. So whatever you have done, the expert will have comments and if quality is good chances are that these comments will be useless. Next time, I’ll put some ducks here and there. Needless to say, if the customer is clueless about the technology, he will take the side of the expert. Which is perfectly understandable, because for that particular manager, not following the expert’s advice can only result in a lot of pain if something happens, even if completely unrelated to the expert’s comments. Another example of C.Y.A.
My third conclusion what that I probably was expecting too much from these experts. To me, the word “expert” means people like those on the top reputation page of StackOverflow.
I have been playing the beta for a while now and maxed out a barbarian.
I have to say that it’s still Diablo, nothing changed much compared to the first two. But that’s why I like it I guess!
Plus the little exclusivity of playing the closed Beta, too!
Today, while coming home from work, I found a printed copy in my post box.
Final day with Io. It’s been quite a ride, and even if I don’t think I grasp everything that Io is, I consider playing with Io a little bit on my own later because of it’s potential, especially in DSL.
Enhance the XML program to add spaces to show the indentation structure
Create a list syntax that uses brackets
curlyBrackets := method( l := List clone call message arguments foreach(arg, l append(doMessage(arg)) ) return l )
Let’s get down to some coding.
Write a method to find the nth Fibonacci number, both iteratively and recursively
Fibonacci := Object clone Fibonacci iter := method(n, l := list(1, 1) for(i, 2, n, 1, l append(l at(i - 2) + l at(i - 1)) ) l at(n - 1) ) Fibonacci rec := method(n, if(n < 3, 1, rec(n - 1) + rec(n - 2) ) ) x := Fibonacci iter(12) x println y := Fibonacci rec(12) y println
How would you change the operator / to return 0 is the denominator is zero
For this one, I was a bit stuck with only the book and the language reference, so I had to Google it. And that’s where you have all the beauty and annoyance of the internet: one of the first hit is a solution to that exact question on StackOverflow (it was to be expected.) I was expecting this exercise to take me to the OperatorTable for which there is an example in the book, but actually the operator already exists so it does not have to be added, but instead re-defined for the Number type.
Write a program to add up all the numbers in a two-dimensional array
a := list(list(1, 2, 3)) a append(list(4, 5, 6, 7, 8, 9)) a append(list(10, 11, 12)) a append(list(13, 14, 15, 16, 17, 18, 19, 20)) total := 0 for(i, 0, a size - 1, b := a at(i) for(j, 0, b size - 1, n := b at(j) total = (total + n) ) ) total println
Add an myAverage slot to a list that computes the average of all the numbers in a list. What happens if there are no numbers in a list? Bonus: raise an exception if any item in the list is not a Number.
a := list(1, 2, 3, 4, 5) b := list(1, "hello") List myAverage := method( total := 0 for(i, 0, self size -1, n := self at(i) if (n type != "Number", Exception raise("An item in the list is not a Number"), total = total + n ) ) total / self size ) a myAverage println b myAverage println
Write a prototype for a two-dimensional list. The dim(x, y) method should allocate a list of y lists that are x elements long. The set(x, y, value) method should set a value and the get(x, y) method should return that value.
Matrix := Object clone Matrix list := nil Matrix dim := method(x, y, self list := List clone for(i, 0, x - 1, l := List clone for(j, 0, y - 1, l append(nil)) self list append(l) ) ) Matrix set := method(x, y, value, self list at(x) atPut(y, value) ) Matrix get := method(x, y, self list at(x) at(y) ) matrix := Matrix clone matrix dim(4, 2) matrix set(3, 1, "Hello") matrix set(1, 1, "World") matrix get(3, 1) println matrix get(1, 1) println
Bonnus: write a transpose method so that new_matrix get(y, x) == matrix get(x, y) on the original list
Matrix transpose := method( x := self list at(0) size y := self list size m := Matrix clone m dim(x, y) for(i, 0, x - 1, for(j, 0, y - 1, m set(i, j, self get(j, i)) ) ) m ) new_matrix := matrix transpose (matrix get(3, 1) == new_matrix get(1, 3)) println
Write the matrix to a file, read the matrix from a file.
For this one, decided to aggressively look in the List API to make the code simpler. I discovered quite a lot of useful functions! I also implemented the asString method so that it is easier to debug (the println method on the Object type is implemented to print the result of asString.)
Here is the full code for the whole Matrix object, improved as much as I could:
Matrix := Object clone Matrix list := nil Matrix dim := method(x, y, self list := List clone setSize(x) for(i, 0, x - 1, self list atPut(i, List clone setSize(y)) ) ) Matrix set := method(x, y, value, self list at(x) atPut(y, value) ) Matrix get := method(x, y, self list at(x) at(y) ) Matrix transpose := method( x := self list at(0) size y := self list size m := Matrix clone m dim(x, y) for(i, 0, x - 1, for(j, 0, y - 1, m set(i, j, self get(j, i)) ) ) return m ) Matrix asString := method( s := "" self list foreach(e, s := s asMutable appendSeq(e join(",")) asMutable appendSeq("\n") ) ) Matrix saveTo := method(file, f := File with(file) remove openForUpdating self list foreach(i, e, f write(e join(",")) write("\n") ) f close ) Matrix loadFrom := method(file, l := List clone f := File with(file) openForReading f readLines foreach(e, l append(e splitNoEmpties(",")) ) f close m := Matrix clone m dim(l size, l at(0) size) l foreach(i, e, e foreach(j, f, m set(i, j, f) ) ) m ) /* Tests */ matrix saveTo("matrix.txt") new_matrix saveTo("new_matrix.txt") matrix := Matrix loadFrom("matrix.txt") new_matrix := Matrix loadFrom("new_matrix.txt") matrix println new_matrix println
Write a program that gives you 10 tries to guess a random number from 1 to 100. Give hints of “Hotter” or “Colder” if you like.
//Not really random, but will suffice for now n := Date now second * 1000 % 100 input := File standardInput(); for(i, 1, 10, guess := input readLine asNumber if (guess < n, "Hotter" println) if (guess > n, "Colder" println) if (guess == n, "Well done!" println break ) )
I really enjoyed myself with these! Looking forward to Day 3…