Implementing ’15 Exercises for Learning a new Programming Language’

A short time ago in a galaxy not so far, far away I came across a nice blog post: 15 Exercises for Learning a new Programming Language.

One could argue if these are *really* the most appropriate 15(+) exercises to learn a new programming language – however, the task of answering this rather complex question is left as an exercise for the reader. Instead of this I will show you their implementation in Ruby – rubyrailways.com style.

Why did I bother to solve these problems (including not really trivial ones, like a scientific calculator with a GUI) ? Well, actually to learn a new programming language! I still consider myself a beginner Ruby apprentice just playing it by ear in my somewhat scarce free time, so I thought that systematically implementing a task list like this will mean great step forward for me compared to just coding random things at random times. Fortunately I was perfectly right!

Before we move onto the code, one last disclaimer: the fact that I am still a Ruby n00b implies that the code can be somewhat hairy/not optimal/[insert any other language than Ruby here]-ish so don’t use these snippets as a textbook solution of the problems or anything like that. I would be glad if someone could suggest a bit of refactoring of the bad parts but I also hope that that there are some nice parts which you can learn from (actually I am quite sure about this since I used some magick formulas from a few Ruby (grand)masters in some cases).

OK, enough talk for now. Let’s see the stuff!

1. Problem: “Display series of numbers (1,2,3,4, 5….etc) in an infinite loop. The program should quit if someone hits a specific key (Say ESCAPE key).”

Solution: Hmm, well, errr…uh-oh… I could not solve this problem fully (what a terrific start :-) ). If Henry Ford would sit beside me now, he would say : You can hit any key to exit – so long as it’s ‘C’ – and one more advice: don’t forget to hold CTRL during this action :-) . More on this after the code snippet:

i = 0
loop { print "#{i+=1}, " }

Comments :
If anyone knows how to add code which will cause this program to stop with a specific keyhit (say ‘ESC’) please, please, please drop me a note. I have been researching this for at least 10% of the time of solving all the tasks, nearly spitting blood when I gave up :-) . It seems (to me) that there is no simple (i.e. no threads and similar) and clean platform-independent solution for this problem. I guess (hope) the author’s idea here was different than to introduce threading or writing platform specific-code…

2. Problem: “Fibonacci series, swapping two variables, finding maximum/minimum among a list of numbers.”

Solution:

#Fibonacci series
Fib = Hash.new{ |h, n| n < 2 ? h[n] = n : h[n] = h[n - 1] + h[n - 2] }
puts Fib[50]

#Swapping two variables
x,y = y,x

#Finding maximum/minimum among a list of numbers
puts [1,2,3,4,5,6].max
puts [7,8,9,10,11].min

Comments: The Fibonacci code was written by Andrew Johnson (found via Ruby Quiz). I like it so much that I think it would be a shame to present a trivial version here. I guess the rest of the code is self-explanatory.

3. Problem: "Accepting series of numbers, strings from keyboard and sorting them ascending, descending order."

Solution:

a = []
loop { break if (c = gets.chomp) == 'q'; a << c }
p a.sort
p a.sort { |a,b| b<=>a }

Comments: This version is accepting strings - I think anybody who got to this point can adapt it to work with numbers.

4. Problem: "Reynolds number is calculated using formula (D*v*rho)/mu Where D = Diameter, V= velocity, rho = density mu = viscosity Write a program that will accept all values in appropriate units (Don't worry about unit conversion) If number is < 2100, display Laminar flow, If it’s between 2100 and 4000 display 'Transient flow' and if more than '4000', display 'Turbulent Flow' (If, else, then...)"

Solution:

vars = %w{D V Rho Mu}

vars.each do |var|
  print "#{var} = "
  val = gets
  eval("#{var}=#{val.chomp}")
end

reynolds = (D*V*Rho)/Mu.to_f

if (reynolds < 2100)
  puts "Laminar Flow"
elsif (reynolds > 4000)
  puts "Turbulent Flow"
else
  puts "Transient Flow"
end

Comments: Can you spot the trick in the part which is filling up the variables? They don't go out of scope after the loop ends because they are constants. Other possibility would be to use $global variables but I guess it is usually not a very good programming practice to do that.

5. Problem: "Modify the above program such that it will ask for 'Do you want to calculate again (y/n), if you say 'y', it'll again ask the parameters. If 'n', it'll exit. (Do while loop)
While running the program give value mu = 0. See what happens. Does it give 'DIVIDE BY ZERO' error? Does it give 'Segmentation fault..core dump?'. How to handle this situation. Is there something built in the language itself? (Exception Handling)"

Solution:

vars = { "d" => nil, "v" => nil, "rho" => nil, "mu" => nil }

begin
  vars.keys.each do |var|
    print "#{var} = "
    val = gets
    vars[var] = val.chomp.to_i
  end

  reynolds = (vars["d"]*vars["v"]*vars["rho"]) / vars["mu"].to_f
  puts reynolds

  if (reynolds < 2100)
    puts "Laminar Flow"
  elsif (reynolds > 4000)
    puts "Turbulent Flow"
  else
    puts "Transient Flow"
  end

  print "Do you want to calculate again (y/n)? "
end while gets.chomp != "n"

Comments: As you can see, I could not use the same trick here when asking for the variables, because when somebody wants to calculate again, Ruby will complain (although by printing a warning only) that the constants have been already set up. Therefore I went for the hash solution. I think the do-you-want-to-calculate-again part is straightforward so I won't analyze that here.

"While running the program give value mu = 0."

Ruby gives a rather interesting result in this case: infinity :-) .

"Is there something built in the language itself?"

Sure: exception handling. Division by zero could be caught with a ZeroDivisionError rescue clause.

6. Problem: "Scientific calculator supporting addition, subtraction, multiplication, division, square-root, square, cube, sin, cos, tan, Factorial, inverse, modulus"

Solution:

Since this code snippet is longer It would look ugly here - you can download it from here instead.

Screenshot:


screenshot of the scientific calculator in action

If you would like to try it, you will need the Tk bindings for Ruby (maybe you have them already, here on Ubuntu I did not). Also note that only the regular 0-9 keys (and of course the mouse) work, the numpad ones do not. One more little detail: % stands for modulo, not percent.

Comments: Phew, this was a real challenge, mostly because I never did any GUI in Ruby before. I was amazed that I could code up a relatively feature rich calculator in 100+ lines of code, without any golfing or trying to optimize for shortness. What I wanted to say with this is that the shortness does not praise my programming skills (since I did not eve try to golf) but the superb terseness of Ruby. OK, of course there are some problems (e.g. cube, cos, tan, inverse are not implemented) but the usability/amount of code ratio is unbelievably high.

The GUI is also not the nicest since I have used Tk - wxRuby or qt-ruby would produce much nicer results, but since I did not code any GUI in Ruby previously, I have decided to try the good-old-skool Tk for the first time.

7. Problem: "Printing output in different formats (say rounding up to 5 decimal places, truncating after 4 decimal places, padding zeros to the right and left, right and left justification)(Input output operations)"

Solution:

#rounding up to 5 decimal pleaces
puts sprintf("%.5f", 124.567896)

#truncating after 4 decimal places
def truncate(number, places)
  (number * (10 ** places)).floor / (10 ** places).to_f
end

puts truncate(124.56789, 4)

#padding zeroes to the left
puts 'hello'.rjust(10,'0')

#padding zeroes to the right
puts 'hello'.ljust(10,'0')

#right justification
puts ">>#{'hello'.rjust(20)}<<"

#left justification
puts ">>#{'hello'.ljust(20)}<<"

Comments: Amazingly lot of things can be done with sprintf() - I could solve nearly all the problems with it - but that would not really be rubyish, so I have decided for built-in (and one homegrown) functions. However, mastering (s)printf() is a very handy thing, since nearly all big players (C (of course :-) ), C++, Java, PHP, ... ) have it so you get a powerful function in more languages for the price of learning one). As you can see, r/ljust is a nice one, too.

8. Problem: "Open a text file and convert it into HTML file. (File operations/Strings)"

Solution: Well, this problem was not specified in a great detail, to say the least - or to put it otherwise, the solvers are given a great freedom to provide a solution spiced up with their fantasy. This is what I came up with:

doc = <strong tag.
DOC

final_doc = <
  
    
  
  
    

embed_doc_here

FINAL_DOC rules = {'*something*' => 'something', '/something/' => 'something'} rules.each do |k,v| re = Regexp.escape(k).sub(/something/) {"(.+?)"} doc.gsub!(Regexp.new(re)) do content = $1 v.sub(/something/) { content } end end doc.gsub!("\n\n") {"

\n

"} final_doc.sub!(/embed_doc_here/) {doc} puts final_doc

Comments: As you can see, besides that the text is wrapped around with a minimal HTML, every occurrence of words between asterisks is outputted in strong and between slashes in italic. You can add as many such rules as you like, they will be (hopefully) substituted in the final output.

9. Problem: "Time and Date : Get system time and convert it in different formats 'DD-MON-YYYY', 'mm-dd-yyyy', 'dd/mm/yy' etc."

Solution: Well, it was not really clear (for me) what should be the difference between 'yyyy' and 'YYYY' (resp. 'dd' vs 'DD') so again I had to use my imagination. However, I guess it does not matter too much, the solution has to be changed by 1-2 characters only if the original author had something different on his mind.

require 'date'

time = Time.now
#'DD-MON-YYYY', e.g. 12-Nov-2006 in my interpetation
puts time.strftime("%d-%b-%Y")

#'mm-dd-yyyy', e.g. 11-12-2006 in my interpetation
puts time.strftime("%m-%d-%Y")

#'dd/mm/yy', e.g. 12/11/2006 in my interpetation
puts time.strftime("%d/%m/%Y")

10. Problem: "Create files with date and time stamp appended to the name"

Solution:

#Create files with date and time stamp appended to the name
require 'date'

def file_with_timestamp(name)
  t = Time.now
  open("#{name}-#{t.strftime('%m.%d')}-#{t.strftime('%H.%M')}", 'w')
end

my_file = file_with_timestamp('test.txt')
my_file.write('This is a test!')
my_file.close

Comments: Maybe a more elegant solution could be to subclass File and override its constructor - but maybe that would be an overkill. I have voted for the latter option in this case :-) .

11. Problem: "Input is HTML table. Remove all tags and put data in a comma/tab separated file."

Solution: Since web extraction is both my PhD topic and my everyday job (and even my free-time activity :-) ) I will present 3 solutions for this problem. First, the classic old-school regexp way (by Paul Lutus), then with HPricot and finally with scRUBYt!, a simple yet powerful Ruby web extraction framework currently developed by me.

table = <
  
    1
    2
  
  
    3
    4
    5
  
  
    6
  

DOC

rows = table.scan(%r{.*?}m)

rows.each do |row|
   fields = row.scan(%r{(.*?)}m)
   puts fields.join(",")
end

Now for the HPricot solution (in the further examples let's consider that table is initialized as in the previous example):

require 'rubygems'
require 'hpricot'

h_table = Hpricot(table)

rows = h_table/"//tr"
rows.each do |row|
  child_text = (row/"//td").collect {|elem| elem.innerHTML }
  puts child_text.join(',')
end

and last, but not least scRUBYt!

require 'scrubyt'

table_data = P.table do
               P.cell '1'
             end

table_data.generalize :cell

puts table_data.to_csv

Some explanation: first of all, at the moment scRUBYt! is avaliable on my hard disk (and partially in my head) only - it should be released around XMAS 2006. I am using this solution for a little bit of self-promotion :-) .

The example works like this: extract something (in this case a HTML <table>) which has something (in this case <td>) which has '1' as its text (well in reality much more is going on in the background, but roughly along these lines). This little code snippet will extract the first <td>s of ALL <tables> on a HTML page. With the 'generalize' call we tell the extractor that it should not extract just the first <td> in a table (which is the default setting), but all of them.

scRUBYt! can handle much, much, MUCH more complicated examples than this (like an ebay or amazon page) and has loads of sophisticated functions... so stay tuned!

12. Problem: "Extract uppercase words from a file, extract unique words."

Solution: (you can find some_uppercase_words.txt here and some_repeating_words.txt here

open('some_uppercase_words.txt').read.split().each { |word| puts word if word =~ /^[A-Z]+$/ }

words = open('some_repeating_words.txt').read.split()
histogram = words.inject(Hash.new(0)) { |hash, x| hash[x] += 1; hash}
histogram.each { |k,v| puts k if v == 1 }

13. Problem: "Implement word wrapping feature (Observe how word wrap works in windows 'notepad')."

Solution: Unfortunately I am not a Windows user and I have seen notepad a *quite* long time ago - so I am not sure the task and it's implementation are fully in-line - I have tried my best. Here we go:

input = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

def wrap(s, len)
  result = ''
  line_length = 0
  s.split.each do |word|
    if line_length + word.length + 1  < len
      line_length += word.length + 1
      result += (word + ' ')
    else
      result += "\n"
      line_length = 0
    end
  end
  result
end

puts wrap(input, 30)

14. Problem: "Adding/removing items in the beginning, middle and end of the array."

Solution:

x = [1,3]

#adding to beginning
x.unshift(0)

#adding to the end
x << 4

#adding to the middle
x.insert(2,2)

#removing from the beginning
x.shift

#removing from the end
x.pop

#removing from the middle
x.delete(2)

#we have arrived at the original array!

15. Problem: "Are these features supported by your language: Operator overloading, virtual functions, references, pointers etc."

Solution: Well this is not a real problem (not in Ruby, at least). Ruby is a very high level language ant these things are a must :) .

Finally, you can download all the solutions in a single archive from here.
I would like to see the implementation of these tasks in both Ruby (different (more optimal) solutions of course) as well as in anything else. If you set out to do something like that, be sure to drop me a note.

Internet contains huge number of opportunities to earn money online. Simply create a site that you think has the potential to sell hot items using ruby on rails. Register a relevant domain name and purchase a web hosting service through hostgator, one of the better web host out there today. Get a internet connection through one of the wireless internet providers to upload your site. Work on search engine optimization to get a better traffic and also use affiliate marketing program for the same reason. Finally get a free voip phone service to contact customers directly. The pc to phone system is the most effective method of marketing.


Short List of the Greatest Inventions of all Time

1. Sliced bread (1928, Otto Frederick Rohwedder)
2. Ruby (1995, Yukihiro “Matz” Matsumoto)
3. HPricot (2006, Why the lucky stiff)
4. Ruby on Rails (2005, David Heinemeier Hansson)

What a relief! I *really* had the urge to tell everybody how cool HPricot is, just did not know the way yet – until now. The cosmic balance is somewhat restored now that I blurted out this post :-) .

Needless to say, you need to take this list with a tiny droplet of humor: Of course if we consider development time, amount and scope of offered solutions, innovation, community, book coverage etc. etc. then Rails is a clear winner (and anyway, the two players are not in the same league). However, HPricot is a great example of how a not-new-at-all thing can be made much more usable, fast and “heaps of fun to use” (really) just by clever design and usage of the right tools (and a dash of a cool programmer’s charisma). It is one thing to come up with a purple cow on a non-saturated market with lots of space for innovation, and a different story to do the same when everything has been already said and done. And _why did it. Again.

I am writing a (not so) small web extraction framework in Ruby (planned release XMAS 2006) which heavily relies on HPricot as the HTML query language – so I dare to say I know (at least some parts of) HPricot pretty well, yet it still keeps me totally amazed. What I like the most about it (besides that it is lightning-on-steroids fast compared to anything available for the same task, feature rich, reliable, stable etc. etc.) is that it takes the ‘principle of least surprise’ to the next level: I would call it ‘principle of almost no surprise’. If someone has a bit of knowledge about org.w3c.dom, XML, XPath, XSLT and/or has experience with other HTML/XML parsers/tools will have to refer to the documentation very rarely (of course there is a period of learning the basics and soaking into the HPricot-philosophy, but the learning curve is *really* steep).

Before I get to the proof that HPricot is able to solve the food problems in Africa or something, I need to cool down a bit :-) : HPricot is not for everyone and not for every problem. If you need complex XPath evaluation for instance, you will have to stick with the good old REXML (for now , at least – I read that _why will add more XPath support and other goodies in the future). In the present version, you won’t be able to evaluate things like axes (e.g. ancestor::html) or XPath functions (e.g. normalize-space) and not even XPaths with indices (like html/body/table[1]/tr[2]/td[5] – though I wrote a small script to remedy this problem temporarily.)

There are a lots of HTML-extraction related questions on the Ruby mailing list (like how to extract every table cell from a <tablle:> etc.) My advice is to alwways check out HPricot first: Sometimes it can be an overkill to use it (if you can get what you want with a simple regexp, for example) but usually it is the right tool to parse and query even the ugliest HTML pages out there- unless you need heavy XPath/XQuery machinery which is rarely the case in the real life.

What else do I need to add? Great job, _why. Thanks man.

How to reach the finish line

Before everything else, getting ready is the secret of success. (Henry Ford)

For the last few months of my professional/geek life I have felt like I am sitting on a huge roller coaster – reaching the sky today just to find myself at the bottom of a cozy swamp tomorrow. Sometimes I have days when I achieve the work of ten people and I am filled with so much energy and enthusiasm that my family is afraid I might blow up in any minute :-) . Even after such a very busy day I usually can not sleep too well because I am thinking/dreaming about how this or that unit test or piece of code could be improved and how will I tackle it tomorrow morning.

However, more frequently then I would like it to happen, the very next day everything may turn out quite the opposite: Working may be mixed with browsing, playing video games, watching TV etc. instead of focused and effective work that was fueling me to the boiling point yesterday.To add to the frustration, it is more or less unpredictable in advance when and why are the ups and downs coming and how long will they persist.

My overall average productivity is about equal with a well balanced person: both of us is working 200 hours a month. However, the balanced guy achieves this by working 50 hours a week whereas my pattern is absolutely unpredictable – it may be 30 + 80 + 20 + 70 or 20 + 30 + 40 + 110 or anything that sums up to 200. I guess 200 + 0 + 0 + 0 did not happen for only single reason yet: because a week has just 168 hours.

Someone could argue: why bother then? After all, the job gets done and that is all what matters. Well, for me this is not the only thing that matters: to effectively pursue a wide range of activities, some kind of planning is needed in order to be able to process them all at once, ensuring that each gets the proper weight at due time. While the hectic model was OK during the campus life (i.e. watch a season of 24 (24 hours), do the work of yesterday and today (20 hours), get some sleep at last (20 hours), rinse, repeat etc), the balanced way of doing things is recommended if you have a family and job yet you still would like to stay involved in a diverse array of activities.

My wife used to laugh at me in the mornings when I put on my loser or winner face. With her constant teasing she made me understand that the fact that how I feel the given day and what I am able to do depends only on me and not on certain circumstances.

My biggest problem seems to be that after the first excitement I tend to easily lose my interest in about everything I start. This of course leads to cooling down and eventually drifting off the track right before the finish line. Why? Well, to jump into some “more interesting stuff” of course. The result: constant feeling of failure and no results to show up. Sad but true.

Since I am an optimistic person and I hate to be in unpleasant situations if I can choose not to, after all that struggling I decided to come up with a plan to “visit” the finish line more often ;-) The points I have identified work for me pretty well so far and I am updating and extending them from time to time based on the results in practice. At the moment this is what I have:

  1. Have a clear vision – if you do not know exactly what do you want to reach or where you want to end up, you will never reach that point. (Even if you would, how would you know? :-)
  2. Make a detailed plan – if you see the progress, it motivates you to continue. You can break down any task to small pieces. By the end of the day, looking at your to-do list you can put up your “I made a great step forward today” smile with reason. I break down even the household chores to small tasks, so after looking at my all-checked to-do list my wife thinks I am the fastest and most effective clean-up guy on the globe ;) . “Nothing is particularly hard if you divide it into small jobs.” (Henry Ford)
  3. Find a partner – even the most excited people lose their enthusiasm over time. A good partner can help you to stand up when you feel like falling or loosing interest and makes you go further if you are just about to give up.
  4. Value yourself and the progress you have achieved – do not be too critical but also do not overestimate yourself. It leads to frustration and ruins your motivation step by step.
  5. Always determine your current level correctly and try to expand from there – it is not the biggest problem if you are weak in certain areas (since that can be improved). However, it is much worse if you don’t admit this to yourself (which means you won’t release any effort to improve it). Once you acknowledge your current position (e.g. that you can work only 10 minutes continuously) you can gradually improve it (e.g. by working without break for 1 more minute daily) until you reach the desired goal (e.g. Alt-Tab-less working for 2 hours). Don’t be ashamed or blame yourself for any clumsiness – identify it and get rid of it!
  6. Do not be impatient – be realistic about the amount of work you are capable to do for the given period of time. There are some things that can be done overnight. (I wanted to finish my Ph.D. in a week and I failed miserably ;-)
  7. Never give up – do not stop before the finish line. Nothing is worst than a work without results. It consumes too much time. By looking at just the achievements (which is usually the practice in the real life) it is useless. No credits, no recognition. Never stop at 99%, since that is still an unfinished job. Even 10 * 99% = 0 (and not 990%, which is 9 by rounding) – thus 1 * 100% > 10 * 99%.
  8. Do not be sorry for yourself – it does not help and drags you down.
  9. Do not look around too often – too many people fail because they look at their surrounding and can not step over the boundaries of the tiny world around them. Who cares if the guy next door is ten times better at bugfixing or writes his papers ten times faster while you are struggling with every word? Even if you may feel this on your own skin, believe me that staring at the abilities of others and the constant comparison leads to a dead end. After some time it makes you believe you are truly useless and stupid. Do not believe everything – probably those “Supermans” are not half as good as they seem to be, and half of the stories about them are urban legends. And what if they are really so superb? Does it change your abilities in any way? Of course not.
    If there is only one point you remember, this be it. Believe me, it can make your life much easier. At least it made mine.
  10. Do not look for excuses – it is the simplest way to get stuck.
  11. Do the interesting and annoying things side by side – it is easy to fall to the “trap of interesting parts”. Unfortunately the most hated parts are also part of the project (at least I have not met a project with solely “fun” parts) and need to be done as well. Do not leave them to the end if you do not want ot struggle just before the finish line. If you do them aside with the fascinating jobs the suffering will “disappear”.

+1 Believe in yourself“To succeed, we must first believe that we can.” (Michael Korda) or as Henry Ford put it: “If you think you can do a thing or think you can’t do a thing, you’re right.”.

Remember: the work you have done is measured by the final result, not the time and effort you have invested in it. Once you start something, never look back – just from behind the finish line.