0

In Learn to Program (2nd Ed) there is an exercise to find leap years between given dates

Write a program that asks for a starting year and an ending year and then puts all the leap years between them (and including them, if they are also leap years). Leap years are years divisible by 4 (like 1984 and 2004). However, years divisible by 100 are not leap years (such as 1800 and 1900) unless they are also divisible by 400 (such as 1600 and 2000, which were in fact leap years). What a mess!

I did some digging and found the 'answer key.' According to the key, the correct code is as follows:

puts 'Input a starting year:'
start_year = gets.chomp
puts 'Input an ending year:'
end_year = gets.chomp
puts ''

while start_year.to_i <= end_year.to_i

  if start_year.to_f % 400 == 0
    puts start_year
  elsif start_year.to_f % 100 == 0
  elsif start_year.to_f % 4 == 0
    puts start_year 
  end

  start_year = start_year.to_i + 1

end

I was wondering why start_year needs to be changed to float, and why there are two elsif statements in a row (% 100 and % 4) without any instructions for what to do after the one with % 100.

It seemed from the instructions that operations dealing with 400 and 100 would need to be grouped, but this seems to have operations dealing with 100 and 4 grouped. For example, this code obviously works and doesn't list 1900 as a leap year, but how does it work?

As I'm looking at the code, when the iteration gets to 1900 (say my start and end dates are 1000 and 2000 respectively), it would fail the first if check but then pass both of the elsifs. So why isn't it part of the output?


Edit

Here is my reformatting of the code. I also made some changes that in my opinion simplified things a bit.

puts 'Input a starting year:'
start_year = gets.chomp.to_i
puts 'Input an ending year:'
end_year = gets.chomp.to_i
puts ''

while start_year <= end_year

  if start_year % 400 == 0
    puts start_year
  elsif start_year % 100 == 0
  elsif start_year % 4 == 0 
    puts start_year
  end

  start_year = start_year + 1

end
Borodin
  • 126,100
  • 9
  • 70
  • 144
sixty4bit
  • 7,422
  • 7
  • 33
  • 57

6 Answers6

4

Your task is very vague, and your proposed answer a distraction.

This is how I would solve the problem in Ruby. Fetching the start and end years is trivial.

start_year = 1999
end_year   = 2050

def leap(year)
  year /= 100 if year % 100 == 0
  year % 4 == 0
end

puts (start_year..end_year).to_a.find_all { |year| leap(year) }

output

2000
2004
2008
2012
2016
2020
2024
2028
2032
2036
2040
2044
2048
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • Not sure what you're referring to with "your proposed answer." Do you mean the one I found in the answer key? As with Mr Rogers below, your answer is ultimately unhelpful because the book hasn't introduced the concepts you're employing. I'm trying to understand what's given in the answer key. – sixty4bit Nov 10 '13 at 03:10
  • @sixty4bit: I thought that had been covered by Peter Alfvin's answer. I was showing a realistic Ruby solutuon. If you are a complete beginner to programming then I believe Ruby is the wrong choice. – Borodin Nov 10 '13 at 03:28
  • I was under the impression from a number of websites that Ruby is a fine choice for complete beginners. Which language would you recommend? (Also, I think I understand the concepts pretty well. But based on my utter confusion with this exercise and your opinion about it, it seems like the problem here is not with me but with the exercises in the book I'm using.) – sixty4bit Nov 10 '13 at 03:52
  • The `to_a` is unnecessary and to be consistent with Ruby conventions, it should be `leap?`. Actually, the _real_ Ruby way would be `Date.leap?` per http://stackoverflow.com/a/1566627/1008891 ;-) – Peter Alfvin Nov 10 '13 at 14:17
  • @sixty4bit I'm not an expert on the pedagogy of computer science or on the pedagogy of computer languages, but in this non-leap year of 2013, I think Ruby is a fine choice for a first programming language. And fortunately, you have many high quality books and resources to draw from. – Peter Alfvin Nov 10 '13 at 14:37
  • @sixty4bit: Ruby is indeed a fine computer language, but it is in essence an object-oriented language from the inside out, which is pretty much unique. Sure, you can write procedural programs using it, but if you have problems with them and bring them here you will be showered with accusations of using non-idiomatic Ruby. Any books you choose to learn from will certainly press the object-oriented aspect. Alternatively, if you learn object-oriented Ruby then you will have a skill that isn't very transportable to other languages. I would suggest Perl or Python as being more "traditional". – Borodin Nov 10 '13 at 19:52
2

There is no reason to convert to float rather than integer.

As for the "empty" elseif clause, it's basically a "no-op" for that case, to avoid the subsequent test for being divisible four. Specifically, the 1900 case satisfies the % 100 == 0 clause, so it doesn't get treated as a leap year.

As an aside, as you may have guessed, this is non-idiomatic Ruby, and not only because of the indentation.

Peter Alfvin
  • 28,599
  • 8
  • 68
  • 106
  • With that being the case, can you explain why 1900 doesn't become part of the output? It seems like it would fail the first if-clause (%400) but pass both of the else-if clauses – sixty4bit Nov 10 '13 at 01:18
  • Please post this as an update to your question so that you can use code formatting. – Peter Alfvin Nov 10 '13 at 01:20
  • Ok, I've added my reformat of the code and changed the question in my comment here. I'm still not exactly following the 'no-op' thing or how this whole is able to work correctly – sixty4bit Nov 10 '13 at 01:22
  • 1
    @sixty4bit: It doesn't evaluate every if/elsif, it starts at the first one and keeps going until something is true, then executes whatever is there for the condition that comes back true. For the elsif start_year % 100, there is nothing for that condition, so nothing gets executed. It then stops evaluating conditions, so it doesn't try the start_year % 4 – Some Guy Nov 10 '13 at 01:46
  • @SomeGuy thank you, I understand it now! I wish your comment were an answer so I could upvote it – sixty4bit Nov 10 '13 at 03:20
  • 1
    @sixty4bit: We were trying to help you to write good Ruby, rather than to understand a dreadful example of the language that you may go on to replicate – Borodin Nov 10 '13 at 03:42
  • @Borodin Thank you. I appreciate that and hope you can understand that I have no frame of reference for some of the answers I've been given. Perhaps I should just find another book? – sixty4bit Nov 10 '13 at 03:51
  • 1
    @sixty4bit I'm not familiar with this author, but the book series is respectable and the Amazon reviews are reasonable if limited. Since I was curious, I searched and found http://learntoprogramanswers.blogspot.com/, which I guess is what you're referring to. This appears to be an anonymous blog post by someone unaffiliated with the author or the publisher, the use of the book cover image notwithstanding. Put another way, I think you ought not to treat anything in there as an "answer". – Peter Alfvin Nov 10 '13 at 14:26
1

This is another way of printing out leap years. I have passed the range as parameter to the function.

def checkleap(rang)
  puts (rang).select{|z| (z % 4 == 0 && z % 100 != 0) || z  % 400 == 0} 
end

checkleap(2000..3000)
Sonu Oommen
  • 1,103
  • 1
  • 10
  • 21
0

I was just answering Chris Pine's tutorial myself and I believe I answered it (as a complete ruby nuby) a bit more simplified than your suggestion:

puts 'What is the starting year?'
starting_year = gets.chomp.to_i
puts 'What is the ending year?'
ending_year = gets.chomp.to_i

while starting_year <= ending_year
  if starting_year%4 == 0 || (starting_year%100 != 0 && starting_year%400 == 0)
    puts starting_year 
  end 
  starting_year+=1
end
stecd
  • 1,681
  • 6
  • 19
  • 28
0

I looked up arrays in ruby docs and found this simple solution:

puts "Starting Year"
syear = gets.chomp
puts "Ending Year"
eyear = gets.chomp
puts ""

lyear = (syear.to_i..eyear.to_i).to_a
lyear.reject!{|x| x % 4 != 0|| (x % 100 == 0 && x % 400 != 0) }

puts lyear

It converts the range to an array. Then removes the elements that don't classify as a leap year.

zishe
  • 10,665
  • 12
  • 64
  • 103
jaysig
  • 71
  • 1
  • 2
  • 7
  • Well, this is almost [the same](http://stackoverflow.com/questions/19885068/use-of-float-and-multiple-elsif-statements-in-leap-year-example/19888703#19888703) but longer – zishe May 10 '14 at 20:16
-1
puts "What is the starting year?"
start_year = gets.chomp
puts "What is the ending year?"
end_year = gets.chomp
  if (start_year.to_i && end_year.to_i)% 4 == 0
   puts "The  start_year is #{start_year.to_i} and end year  is{end_year.to_i}"
  for leap in start_year.to_i..end_year.to_i
    if leap % 4 == 0
        puts "The leap year is #{leap}"
    else 
          puts "No leap year"
    end
  end
end
Adam Michalik
  • 9,678
  • 13
  • 71
  • 102
rainbow
  • 1
  • 1