Learn to Program, by Chris Pine

Just one more way, since I haven’t seen a solution that takes advantage
of the no-parameter inject call:

1234.to_s.split(’’).map {|c| c.to_i }.inject {|i,j| i+j }
=> 10

Jan_K wrote:

Chapter 9, exercise 1 (page 76)

Perfect. I can’t see any problems at all.

Let me add something, though. Instead of chomping the string, you can
strip it. String#strip removes all whitespace from the beginning and end
of the string, rather than just a single \n from the end.

Cheers,
Dave

Chapter 9, exercise 2 (page 76)

Old-school Roman numerals. In the early days of Roman numerals,
the Romans didn?t bother with any of this new-fangled subtraction
IX nonsense. No sir, it was straight addition, biggest to littlest -
so 9 was written VIIII, and so on. Write a method that, when
passed an integer between 1 and 3000 (or so), returns a string
containing the proper old-school Roman numeral. In other words,
old_roman_numeral 4 should return ‘IIII’. Make sure to test
your method on a bunch of different numbers. Hint: Use the integer
division and modulus methods on page 36.
For reference, these are the values of the letters used:
I = 1 V = 5 X = 10 L = 50
C = 100 D = 500 M = 1000

Solution:

def old_roman_number input

while input < 1 || input > 3999
puts ‘Please enter a number between 1 and 3999’
input = gets.chomp.to_i
end

m_mod = input%1000
d_mod = input%500
c_mod = input%100
l_mod = input%50
x_mod = input%10
v_mod = input%5

m_div = input/1000
d_div = m_mod/500
c_div = d_mod/100
l_div = c_mod/50
x_div = l_mod/10
v_div = x_mod/5
i_div = v_mod/1

m = ‘M’ * m_div
d = ‘D’ * d_div
c = ‘C’ * c_div
l = ‘L’ * l_div
x = ‘X’ * x_div
v = ‘V’ * v_div
i = ‘I’ * i_div

puts m + d + c + l + x + v + i

end

number = gets.chomp.to_i
old_roman_number(number)

Chapter 9, exercise 3 (page 76)

Modern Roman numerals. Eventually, someone thought it would
be terribly clever if putting a smaller number before a larger one
meant you had to subtract the smaller one. As a result of this
development, you must now suffer. Rewrite your previous method
to return the new-style Roman numerals, so when someone calls
roman_numeral 4, it should return ‘IV’.

Solution:

def roman_numeral input

while input < 1 || input > 3999
puts ‘Please enter a number between 1 and 3999’
input = gets.chomp.to_i
end

m_mod = input%1000
d_mod = input%500
c_mod = input%100
l_mod = input%50
x_mod = input%10
v_mod = input%5

m_div = input/1000
d_div = m_mod/500
c_div = d_mod/100
l_div = c_mod/50
x_div = l_mod/10
v_div = x_mod/5
i_div = v_mod/1

m = ‘M’ * m_div
d = ‘D’ * d_div
c = ‘C’ * c_div
l = ‘L’ * l_div
x = ‘X’ * x_div
v = ‘V’ * v_div
i = ‘I’ * i_div

if i == ‘IIII’ && v != ‘V’
i = ‘IV’
elsif i == ‘IIII’
v = ‘IX’
i = ‘’
end

if x == ‘XXXX’ && l != ‘L’
x = ‘XL’
elsif x == ‘XXXX’
l = ‘XC’
x = ‘’
end

if c == ‘CCCC’ && d != ‘D’
c = ‘CD’
elsif c == ‘CCCC’
d = ‘CM’
c = ‘’
end

puts m + d + c + l + x + v + i

end

number = gets.chomp.to_i
roman_numeral(number)

Hello,

thank you very much for all these good answers
(especially to Jan_K and Dave B. ).

Im learning ruby with the same book and get stuck
in chapter 10.2 Rite of Passage:Sorting
this exercise is about array sort algorithms.
Instead of ‘array.sort’ i have to program my own sort method.

chris pine gives a few hints:
using < to find the smallest word in a list

take two lists in the method

and these two empty methods

def sort some_array
recursive_sort some_array, []

end

def recursive_sort unsorted_array, sorted_array

end

After a lot of trying and searching …

…I found the quicksort algorithm and this code

def quicksort( array )
if array.size <= 1
array
else
ls, rest = array.partition { |i| i < array[0] }
puts ‘ls:’+ls.to_s
puts ‘rest:’+rest.to_s
rs, rest = rest.partition { |i| i > array[0] }
puts ‘rs:’+rs.to_s
puts ‘rest2:’+rest.to_s
quicksort( ls ) + rest + quicksort( rs )

end

end

Yes this is a possible solution, but array.partion ,double
assignments(ls,rest)
of variables,arrays and the ‘short block’ (like { |i| i > array[0] })
are not mentioned in any chapter before.

Although I understand the Code above , I’m looking for an easy solution
that don’t leave the scope of chaper 1 to 10 .

Here’s my own code, but it doesn’t work .

def ownsort unsort, sort
rest=[]
rs=[]
if unsort.size <= 1
unsort
else
unsort.each do |i|
if i <unsort[0]

   sort.push i
 else
   rest.push i
 end

end

end
rest
end

thanks for reading and hopefully some hints…

colin

I’m not quite sure there are solutions. However, as a fellow beginning
programmer, I would enjoy discussing our solutions since I finished that
book only one week ago.

Cheers,

Marco

Restricting to what is covered in chapters 1-10, how about the
following?

def sort some_array
recursive_sort some_array, []
end

def recursive_sort unsorted_array, sorted_array
if unsorted_array.size == 0
return sorted_array
else
smallest = unsorted_array[0]
unsorted_array.each do |x|
if x < smallest
smallest = x
end
end
new_unsorted_array = []
unsorted_array.each do |x|
if x == smallest
sorted_array.push(x)
else
new_unsorted_array.push(x)
end
end
recursive_sort new_unsorted_array, sorted_array
end
end

On Thursday, March 30, 2006, at 6:33 AM, Jan_K wrote:

I feel like beating the shit out of some punching bag somewhere. Is
this a normal reaction when one is trying to learn to program for the
first time?

Not exactly, but I suppose it depends on the person. Perhaps you need
to take a page out of the carpenters handbook and walk away for a
while, then come back to the problem.

Jan


Jeremy T.
[email protected]

“You cannot depend on your eyes, when your imagination is out of
focus.” – Mark Twain

Jeff de Vries wrote:

Restricting to what is covered in chapters 1-10, how about the
following?

Thank you Jeff,
Your Code looks great and I think it is exactly what I’m looking for.

Next I will try to answer your question about the Restriction.

The first ten chapters of the book covers
only a few array methods like each,join,last,length,pop.
no partition, no selection or double assignment.
Futhermore we have if -else,elsif- end, do-end,while-end,puts, gets
(with chomp,downcase),array=[],times(3.times do…)
we have basics like numbers and strings ,variables assignment,
comparison methods (==,!=,<,>…),writing own methods (def- end)

In Chapter 11-14 the author talks about:

reading and writing files, creating classes,YAML,Blocks and Procs
and a hint to other learning resources like Programming Ruby or this
mailing-list…

The book is a great starting point with many exercises.
It is great to learn programming. It’s explains Basic ‘Thinking-Skills’
of
programming and how ruby works.

The author provides only one way to do something. This limitation is
great.
I have no problems with the excercises until chapter 10.
And Chapter 11 should be not so hard to understand (hopefully…)

BTW:
The book is the extended Version of this Tutorial:

http://pine.fm/LearnToProgram/

greetings

Colin

For interest, before I bought the book I did this exercise for the
online tuorial. That was before I knew about recursives, here is what
I came up with… it works, but it’s not pretty

words_array = []
puts ‘Please enter any word’
word = gets.chomp

while word != ‘’
words_array.push word
puts ‘Great, please enter another word’
word = gets.chomp
end

sorted_array = []

words_array.each do |word|
if word.to_s.downcase > sorted_array.last.to_s.downcase
#add word to the end of sorted_array
sorted_array.push word

else
index = (sorted_array.length) - 1
initial_number = index
temp_array = []

#check word against next word in sorted_array and repeat
while word.to_s.downcase < sorted_array[index].to_s.downcase
  #add the sorted_array to a temp_array up to that point
  temp_array[index] = sorted_array[index]
  #erase words in sorted_array up to that point
  sorted_array.pop
  index = index - 1
end

#add the word to sorted_array
sorted_array.push word

#add one by one the words from temp_array to sorted_array
while index < initial_number
  sorted_array.push temp_array[index + 1]
  index = index + 1
end

end
end

puts sorted_array

I’m having some issues modifying the Civ continent example from ch. 10
to account for coordinates that are outside the array. I added the
following line:

if ((y <= 10 && y > 0) && (x <= 10 && x > 0))

before…

So, first we count this tile…

size = 1
world[y][x] = ‘counted land’

# ...then we count all of the
# neighboring eigth tiles (and,
# of course, their neighbors via recursion)
size = size + continent_size(world, x-1, y-1) #4,4
size = size + continent_size(world, x  , y-1) #5,4
size = size + continent_size(world, x+1, y-1) #6,4
size = size + continent_size(world, x-1, y  ) #4,5
size = size + continent_size(world, x+1, y  ) #6,5
size = size + continent_size(world, x-1, y+1) #4,6
size = size + continent_size(world, x  , y+1) #5,6
size = size + continent_size(world, x+1, y+1) #6,6
size

end

As a result, I get the following error:

TypeError: nil can’t be coerced into Fixnum

method + in civilization.rb at line 35
method continent_size in civilization.rb at line 35
method continent_size in civilization.rb at line 35
method continent_size in civilization.rb at line 36
method continent_size in civilization.rb at line 35
method continent_size in civilization.rb at line 40
method continent_size in civilization.rb at line 39
method continent_size in civilization.rb at line 41
method continent_size in civilization.rb at line 39
method continent_size in civilization.rb at line 41
method continent_size in civilization.rb at line 39
method continent_size in civilization.rb at line 36
method continent_size in civilization.rb at line 35
at top level in civilization.rb at line 48

This error appears regardless of whether the arguments I pass are in 5,5
(as in the example Chris P. wrote out) or something like 10,10 (which
is on the edge of the ‘continent’).

Is that the right conditional statement? Could it be in the wrong place
in the method?

Hi Mike,

I don’t know the book and that is probably true for a lot of the
people here. Maybe you should include the full source code.
Especially interesting would be to see the line numbers as the
interpreter clearly indicates where the problem occurs.

As a totally wild guess … The error message says that the right
side of an addition is nil. So looking at your code, “size” seems to
be set, maybe continent_size does return nil?

What I also think is strange is that “world” seems to be a two
dimensional array when you first mention it (world[x][y] = ‘counted
land’), but later on you pass it to continent size along with two
parameters. This might be ok, if continent_size defines three
parameters, like this:

def continent_size(world, x, y); end

and you use x and y to access the array then?!

I don’t know about the conditional. What are you trying to check?
That both x and y are between 1 and 10? That should work … Even
though you wouldn’t need the braces as it all of the conditionals
need to be true for the whole expression to become true. This would
be different if you use “or”.

Hope that helps!

Cheers,
Mariano

here’s the whole code…

These are just to make the map easier to read. “M” is

visually more dense than “o”.

M = ‘land’
o = ‘water’

world = [[M,o,o,o,o,o,o,o,o,o,M],
[o,M,M,o,M,M,o,o,o,M,M],
[o,o,M,M,o,o,o,o,M,M,o],
[o,o,o,M,o,o,o,o,o,M,o],
[o,o,o,M,o,M,M,o,o,o,o],
[o,o,o,o,M,M,M,M,o,o,o],
[o,o,o,M,M,M,M,M,M,M,o],
[o,o,o,M,M,o,M,M,M,o,o],
[o,o,M,M,o,o,M,M,M,o,o],
[o,M,M,M,o,M,o,o,o,M,M],
[M,o,o,o,o,o,o,o,o,o,M]]

def continent_size world, x, y
if world[y][x] != ‘land’
# either it’s water or we’ve already counted it; we don’t want to
count it
# again
return 0
end

if ((y <= 10 && y > 0) && (x <= 10 && x > 0))

So, first we count this tile…

size = 1
world[y][x] = ‘counted land’

# ...then we count all of the
# neighboring eigth tiles (and,
# of course, their neighbors via recursion)
size = size + continent_size(world, x-1, y-1)
size = size + continent_size(world, x  , y-1)
size = size + continent_size(world, x+1, y-1)
size = size + continent_size(world, x-1, y  )
size = size + continent_size(world, x+1, y  )
size = size + continent_size(world, x-1, y+1)
size = size + continent_size(world, x  , y+1)
size = size + continent_size(world, x+1, y+1)
size

end
end

puts continent_size(world, 5, 5) #this should be fine; but what about
(11,11)?

Hi Mike,

what your guard should do is prevent the recursion go across the
boundaries of your world, right?

I would put the guard at the beginning of the method to make sure
that the rest of the method can be sure of handling valid data.

if x < 0 || x > 10 || y < 0 || y > 10
return 0 # respect the world’s boundaries
end

def continent_size world, x, y
if world[y][x] != ‘land’
# either it’s water or we’ve already counted it; we don’t want to
count it
# again
return 0
end
That’s a bit of a problem as your code might already call with out of
bounds coordinates.
Put the boundary guard (see above) before that.

if ((y <= 10 && y > 0) && (x <= 10 && x > 0))
An array starts to count at zero … so it needs to be (y <= 10 && y
= 0), right?
You have eleven columns and eleven rows. So the array indices are 0…10.

size = size + continent_size(world, x+1, y-1)
size = size + continent_size(world, x-1, y  )
size = size + continent_size(world, x+1, y  )
size = size + continent_size(world, x-1, y+1)
size = size + continent_size(world, x  , y+1)
size = size + continent_size(world, x+1, y+1)
size

Ok, you’re returning the size here, but

end
what are you returning here, if you haven’t been in this if-block?

nil. That is a major part of your problem…

end
Use the code above and put it after the definition of the method.
Remove your own “if” and your method should work.

puts continent_size(world, 5, 5) #this should be fine; but what about
(11,11)?
That’s up to you. In my code (the guard) it returns 0. So that 11,11
will return 0.
You could also raise an exception that the coordinates are out-of-
bounds, but I guess that will come later in your book and is not that
straight forward applicable here.

Cheers,
Mariano

That worked! Thanks.

-Mike

He Fa:

Good Job! However, I don’t fully understand how this worked.

words_array.each do |word|
if word.to_s.downcase > sorted_array.last.to_s.downcase
#add word to the end of sorted_array
sorted_array.push word

else
index = (sorted_array.length) - 1
initial_number = index
temp_array = []

The second WHILE section was where I got really confused.

#check word against next word in sorted_array and repeat
while word.to_s.downcase < sorted_array[index].to_s.downcase
  #add the sorted_array to a temp_array up to that point
  temp_array[index] = sorted_array[index]
  #erase words in sorted_array up to that point
  sorted_array.pop
  index = index - 1
end

#add the word to sorted_array
sorted_array.push word

#add one by one the words from temp_array to sorted_array
while index < initial_number
  sorted_array.push temp_array[index + 1]
  index = index + 1
end

end
end

puts sorted_array

I’ve been wrapping my head around my own version of the non-recursive
sort for days and haven’t made it work.

Hello all. Thanks to all the posters who came before me! You’ve been
tremendous help. I have a quick question on Chapter 11 - Reading and
Writing, Section 11.3:

file_name = 'TestFile.txt' test_string = 'Nothing!'

File.open file_name, ‘w’ do |f|
f.write test_string
end

read_string = File.read file_name
puts(read_string == test_string)

I poked around the web, but I can’t find out what the ‘w’ is for. Can
anyone shed any light? Muchas gracias in advance.

-Todd

I poked around the web, but I can’t find out what the ‘w’ is
for. Can anyone shed any light? Muchas gracias in advance.

http://dev.rubycentral.com/ref/ref_c_file.html#open

the ‘w’ option opens the file for writing.

  • donald

On 6/13/07, Tm Bo [email protected] wrote:

end

read_string = File.read file_name
puts(read_string == test_string)

I poked around the web, but I can’t find out what the ‘w’ is for. Can
anyone shed any light? Muchas gracias in advance.

It’s the file ‘mode’. w = write.

On Thu, Jun 14, 2007 at 06:04:25AM +0900, Tm Bo wrote:

end

read_string = File.read file_name
puts(read_string == test_string)

I poked around the web, but I can’t find out what the ‘w’ is for. Can
anyone shed any light? Muchas gracias in advance.

Check out this page:

http://www.rubycentral.com/book/ref_c_file.html

Looking at it, I see that File.open is sometimes a synonym for File.new,
so I checked File.new, and that’s where I found documentation of the use
of characters in that position in the method call.

The w itself basically means “write”. It refers to how you open the
file
(for reading, writing, or both). Hopefully this gives you a helpful
start on figuring out the rest of the details of how File.new and
File.open work.

I was surprised to find that File.open isn’t in my local ri database,
though.