Learn to Program, by Chris Pine

Benjohn Barnes wrote:

Top programming tip - it's amazing how often trying
to explain a problem will suddenly make you realise what the answer
is. Curious, but true.

This is incredibly true. I often will describe my programming problems
to non-programmers who would have essentially no chance of helping me,
but end up doing so just by listening.

An interesting anecdote:

I've been told that there is (or used to be) a rule at the MIT
programming help desk that before you could talk to any of the techs,
you had to fully describe your problem to a stuffed animal called the
"problem bear". It turned out that more than half of the people's
problems were solved by the bear.

Jan K wrote:

I might continue posting the solutions for the rest of the book if I
decide to keep on reading.

This way I won't develop any bad habits (because hopefully the
solutions will be scrutinized by at least one other person) and it'll
help anyone else reading the book who gets stuck. I doubt the author
of the book will ever get around to publishing the "official"
solutions.

I just looked at the book samples at pragprog and was surprised to see there
are no solutions. That probably means Chris doesn't intend to do official
solutions. Ah, well. Your points are all valid, posting solutions is a good
idea all 'round.

Now, scrutiny and no bad habits. You've only made one mistake, but I'll
point out a couple of things that could be done differently.

puts ''

That's the same as just "puts" by itself, with no parameters.

input = input.to_i - 1

You can avoid going backwards here by moving "input = input + 1" to the end
of the loop. It often makes things simpler to do the increment at the end
rather than the beginning of a loop.

if input%4 == 0 || input%400 == 0 && input%100 != 0

Here's the mistake.

1984 and 2004 are leap years. OK.
1800 and 1900 are not leap years. Not OK - this program lists them as leap
years.
1600 and 2000 are leap years. OK.

In case this info helps, && binds more closely than ||, so what you've got
is the same as:

if input%4 == 0 || (input%400 == 0 && input%100 != 0)

You should be able to rearrange it to give the correct result for the 1984,
2004, 1800, 1900, 1600 and 2000.

Cheers,
Dave

Jan_K wrote:

Solution for the 1st exercise in chapter 8:

You mean chapter 7.

(I'm skipping the 2nd exercise - just a bunch of busywork)

-----------------------------------------------------------------------
input =

while input.last != ''
  input.push gets.chomp
end

puts input.sort
-----------------------------------------------------------------------

Very nice!

For exercise 2, writing a program that does something useful is good practice, and a sort algorithm's simple and therefore a good candidate if you can get into it.

Or you could just write a game (Blackjack's simple) after you finish chapter 8.

Cheers,
Dave

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

Hello,

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

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

···

--
Posted via http://www.ruby-forum.com/.

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.

···

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

Jan

--
Jeremy Tregunna
jtregunna@blurgle.ca

"You cannot depend on your eyes, when your imagination is out of focus." -- Mark Twain

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

the 'w' option opens the file for writing.

- donald

···

-----Original Message-----
From: list-bounce@example.com
[mailto:list-bounce@example.com] On Behalf Of Tm Bo
Sent: Wednesday, June 13, 2007 4:04 PM
To: ruby-talk ML
Subject: Re: Learn to Program, by Chris Pine

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:

<quote>
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)
</quote>

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.

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:

<quote>
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)
</quote>

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.
http://www.zenspider.com/Languages/Ruby/QuickRef.html#15

···

On 6/13/07, Tm Bo <vineire77@gmail.com> wrote:

-Todd

--
Posted via http://www.ruby-forum.com/\.

--
Bill Guindon (aka aGorilla)
The best answer to most questions is "it depends".

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.

···

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

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:

<quote>
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)
</quote>

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.

--
CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]
Patrick J. LoPresti: "Emacs has been replaced by a shell script which 1)
Generates a syslog message at level LOG_EMERG; 2) reduces the user's disk
quota by 100K; and 3) RUNS ED!!!!!!"

This is my take on the same problem:

def roman_num number
set1 = [ 1, 5, 10, 50, 100, 500, 1000 ]
set2 = [ 'I', 'V', 'X', 'L', 'C', 'D', 'M' ]
numeral =
  while number > 0
    if (number/(set1.last)) >= 1
      roman = (number/(set1.last))
      numeral.push((set2.pop)*roman)
      number = (number%(set1.pop))
    else
      set2.pop
      set1.pop
    end
  end
  puts 'Old Roman Numeral is ' + numeral.join + '.'
end

puts 'Please enter a number to see what it is in old roman numerals.'
number = gets.chomp.to_i
while number < 1 || number > 3999
  puts 'Please enter a number between 1 and 3999'
  number = gets.chomp.to_i
end
roman_num number

Jan_K wrote:

···

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)

--
Posted via http://www.ruby-forum.com/\.

Pine's book is, for it's price, rubbish! You can find better on the web for free. And this applies to most of the Ruby books out there
See how many actually create a full program with an interface?

···

Date: Thu, 9 Aug 2012 16:44:13 +0900
From: lists@ruby-forum.com
Subject: Re: Learn to Program, by Chris Pine
To: ruby-talk@ruby-lang.org

Jan_K wrote in post #55253:
> Does anyone know where I can find the solutions to the exercises in
> this book:
>
> Learn to Program, by Chris Pine
>
>
> I'm stuck in "Flow Control" chapter, specificailly the "Extend Deaf
> Grandma" exercise.
>
> 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?

Hi: I googled the book and answers and here's where they are:

Learn to Program, Answers: Learn to Program Answer Key

--
Posted via http://www.ruby-forum.com/\.

Hi! I'm newbie and this are my first steps. This is my solution for
Modern Roman numerals using arrays and each.

<!-- Code -->

romans =
[1000,'M'],[900,'CM'],[500,'D'],[400,'CD'],[100,'C'],[90,'XC'],[50,'L'],[40,'XL'],[10,'X'],[9,'IX'],[5,'V'],[4,'IV'],[1,'I']
result = []

puts "Please enter a number"
num = gets.chomp.to_i

print "\n" + num.to_s + " in Modern Roman Numerals is: "

romans.each do |nroman|
  number = nroman[0]
  letter = nroman[1]

  while num >= number
    result.push letter
    num -= number
  end

end

print result.join

<!-- #Code -->

cheers!!

Martin

···

--
Posted via http://www.ruby-forum.com/.

I can't use "Until" because it wasn't covered in the book so far (even
though it seems pretty straightforward what it does). Ditto for "+-"

What was covered so far up until this chapter:

if, else, elsif, while

The first thing I tried was nesting IF's and WHILE's in different
combinations without using a variable to track the "BYE"s. I thought
that this would be the elegant solution, but really couldn't come up
with a way to do it.

So then I falled back to my first idea of "use variable to track
BYE's." But it looks like I'm not understanding the rules of nesting
and when/how a branch finishes and kicks you back to the previous
branch.

Here's what I had when I gave up:

···

On Thu, 30 Mar 2006 07:03:17 +0900, "Jeppe Jakobsen" <jeppe88@gmail.com> wrote:

forgot to make it set counter to 0 if it wasn't "BYE", but I'm sure you can
figure that out using else.

2006/3/29, Jeppe Jakobsen <jeppe88@gmail.com>:

You could use until or if (or both). You could make a variable the would
count how many times in a row you said "BYE" to grandma, starting at 0 of
cause, and make it add 1 each times and reset it to 0 if you did not say
"BYE".

Here is an example:

counter = 0

Until (counter == 3)
   tell_grandma = gets.chomp
   if (tell_grandma == "BYE")
      counter += 1
   end
end

Just find a way to integrate with your current grandma program :slight_smile:

------------------------------------------
input = gets.chomp
number = rand(21)

number2 = 0
keep_count = 0

if keep_count != 3
  while
    input != 'BYE'
    puts 'HUH?! SPEAK UP, SONNY!'
    keep_count = 0
    input = gets.chomp
      while
            input == 'BYE'
        puts 'HUH?! SPEAK UP, SONNY!'
        number2 = (keep_count + 1)
        keep_count = number2
        input = gets.chomp
      end
  end
else
puts 'NO, NOT SINCE ' + (1930 + number).to_s + '!'
end
-----------------------------------------

And here's the regular Deaf Grandma solution that I used:

----------------------------------------
input = gets.chomp
number = rand(21)

if
  while
    input != 'BYE'
    puts 'HUH?! SPEAK UP, SONNY!'
    input = gets.chomp
  end
else
  puts 'NO, NOT SINCE ' + (1930 + number).to_s + '!'
end
-------------------------------------

Thanks.

--
Jan

"Dave Burt" <dave@burt.id.au> writes:

JB wrote:

I'm stuck trying to do the part to make it do '99 bottles of beer on the
wall' song. I get started in what I hope will work and next thing I know I
have way too many variables and nothing makes sense. I even did something
that made it loop the integer 0. I had to Ctrl-c on that one. <sigh>

Here's what I quit trying at, because no matter how many breaks I took or
whatever, it just wasn't making any sense. :
...
I too wish the author had put examples, say on some pages in the back or
something.

But if you ask this group, you can get interactive answers!

Let's start again with that one. We need a loop counting down from 99.

And therefore, we write in Ruby:

99.downto(1) { |bottles| ... }

···

Cheers,
Dave

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

Crap, I knew I should have fully tested my code.

<snip>

if input%4 == 0 || input%400 == 0 && input%100 != 0

Here's the mistake.

1984 and 2004 are leap years. OK.
1800 and 1900 are not leap years. Not OK - this program lists them as leap
years.
1600 and 2000 are leap years. OK.

In case this info helps, && binds more closely than ||, so what you've got
is the same as:

if input%4 == 0 || (input%400 == 0 && input%100 != 0)

You should be able to rearrange it to give the correct result for the 1984,
2004, 1800, 1900, 1600 and 2000.

Yep.

if input%100 != 0 && input%4 == 0 || input%400 == 0

Fix:

···

On Sat, 01 Apr 2006 02:03:13 GMT, "Dave Burt" <dave@burt.id.au> wrote:

-------------------------------------------------------------------------------
puts 'Please enter the starting year'
input = gets.chomp
puts 'Please enter the ending year'
input2 = gets.chomp
puts
puts 'The following are leap years:'

input = input.to_i

while input <= input2.to_i
     if input%100 != 0 && input%4 == 0 || input%400 == 0
      puts input.to_s
    end
  input = input + 1
end
-------------------------------------------------------------------------------

1 line shorter at the expense of readability and performance:

-------------------------------------------------------------------------------
puts 'Please enter the starting year'
input = gets.chomp
puts 'Please enter the ending year'
input2 = gets.chomp
puts
puts 'The following are leap years:'

while input.to_i <= input2.to_i
    if input.to_i%100 != 0 && input.to_i%4 == 0 || input.to_i%400 == 0
  puts input.to_s
    end
input = input.to_i + 1
end
-------------------------------------------------------------------------------

I actually did a little benchmark calculating all the leap years for
the next 100 million years (with output going to nul).

Results:

13:26 minutes for the first one.
14:56 minutes for the second one.

The shorter code was 11.16% slower.

I'm actually reading the book and "Arrays and Iterators" is chapter 8.

The book starts with chapter 1 whereas the online tutorial starts with
chapter 0.

Now that I take a look at both it seems like the book is a cleaned-up
and slightly revised version of the tutorial.

Here's the end of chapter 8 from the book:

···

On Sun, 02 Apr 2006 03:57:21 GMT, Dave Burt <dave@burt.id.au> wrote:

Jan_K wrote:

Solution for the 1st exercise in chapter 8:

You mean chapter 7.

----------------------------------------------------------------------------------
8.3 A Few Things to Try

• Write the program we talked about at the beginning of this chapter,
one that asks us to type as many words as we want (one word
per line, continuing until we just press Enter on an empty line)
and then repeats the words back to us in alphabetical order. Make
sure to test your program thoroughly; for example, does hitting
Enter on an empty line always exit your program? Even on the
first line? And the second? Hint: There’s a lovely array method
that will give you a sorted version of an array: sort. Use it!

• Rewrite your table of contents program on page 35. Start the
program with an array holding all of the information for your table
of contents (chapter names, page numbers, etc.). Then print out
the information from the array in a beautifully formatted table of
contents.
----------------------------------------------------------------------------------

This the end of the same chapter from the online tutorial:
----------------------------------------------------------------------------------
A Few Things to Try

• Write the program we talked about at the very beginning of this
chapter.
Hint: There's a lovely array method which will give you a sorted
version of an array: sort. Use it!

• Try writing the above program without using the sort method. A large
part of programming is solving problems, so get all the practice you
can!

• Rewrite your Table of Contents program (from the chapter on
methods). Start the program with an array holding all of the
information for your Table of Contents (chapter names, page numbers,
etc.). Then print out the information from the array in a beautifully
formatted Table of Contents.
----------------------------------------------------------------------------------

Some crucially important differences:

From the online tutorial:
----------------------------------------------------------------------------------
languages = ['English', 'German', 'Ruby']

languages.each do |lang|
  puts 'I love ' + lang + '!'
  puts 'Don\'t you?'
end

puts 'And let\'s hear it for C++!'
puts '...'
----------------------------------------------------------------------------------

From the book:
----------------------------------------------------------------------------------
languages = [' English' , ' Norwegian' , ' Ruby' ]

languages.each do |lang|
  puts ' I love ' + lang + ' !'
  puts ' Don\' t you?'
end

puts ' And let\' s hear it for Java!'
puts ' <crickets chirp in the distance>'
----------------------------------------------------------------------------------

Actually, I just flipped through the rest of the book and it starts to
diverge quite a bit from the online tutorial starting with the next
chapter and has 4 additional new chapters.

Here are the table of contents from the book:
http://pragmaticprogrammer.com/titles/fr_ltp/index.html

So if I have any more questions I'll post the exercise instructions
first.

Thanks for your help so far Dave. I was completely stuck in that Flow
Control chapter and pretty much became content that another attempt at
learning programming has successfully failed (just like the previous
half dozen times). It's amazing how hard it is to grasp incredibly
fucking simple concepts for the first time.

···

On Sun, 02 Apr 2006 03:57:21 GMT, Dave Burt <dave@burt.id.au> wrote:

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 Apr 29, 2006, at 11:38 AM, Colin wrote:

Hello,

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

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

--
Posted via http://www.ruby-forum.com/\.

Thanks, donald. I don't know why I missed that before.

···

--
Posted via http://www.ruby-forum.com/.

Just for fun. Certainly not great for speed for large numbers, but the
integer max was low so...

H = Hash[*(([1,5,10,50,100,500,1000].zip %w|I V X L C D M|).flatten)]
def roman(n, s="")
  H.keys.sort.reverse.each do |k|
    s << (H[k] * (n / k))
    n %= k
  end
  s
end
puts roman(ARGV[0].to_i)

The usage would simply be "ruby <filename>.rb <number>"

I'm sure someone could come up with a one-liner, though.

Todd

···

On Feb 2, 2008 1:33 PM, Kelly Tanguay <kelly.tanguay@cox.net> wrote:

This is my take on the same problem:

def roman_num number
set1 = [ 1, 5, 10, 50, 100, 500, 1000 ]
set2 = [ 'I', 'V', 'X', 'L', 'C', 'D', 'M' ]
numeral =
  while number > 0
    if (number/(set1.last)) >= 1
      roman = (number/(set1.last))
      numeral.push((set2.pop)*roman)
      number = (number%(set1.pop))
    else
      set2.pop
      set1.pop
    end
  end
  puts 'Old Roman Numeral is ' + numeral.join + '.'
end

puts 'Please enter a number to see what it is in old roman numerals.'
number = gets.chomp.to_i
while number < 1 || number > 3999
  puts 'Please enter a number between 1 and 3999'
  number = gets.chomp.to_i
end
roman_num number