你们可能已经注意到有些方法当你在调用它们时会返回来一些东西。举例来说:gets
会返回一个字符串(就是你所键入的内容),在5+3
中+
方法(实际是5
.+(3)
)会返回来8
.数字间的运算方法返回的也会是数字,字符串间运算方法返回的也会是字符串。
要分辨清楚方法被调用后的返回值和程序输出到屏幕上的信息这两者间的区别是很重要的。比如方法puts 5+3
中,5+3
返回值是数字8,而不是字符串8。
而puts
的返回值会是什么?之前我们一直没考虑到过,那么现在让我们来思考一下:
This puts returned:
第一个puts
看似没返回任何值,但只是在某种程度上它没有返回值,它实际上返回的是nil
;尽管我们没测试第二个puts
,它返回的也是puts
,实际上puts
总会返回nil
。所有的方法必须会有一个返回值,即使返回值是nil
。
稍微休息一会儿,写一个小程序来测试一下sayMoo
会返回什么?
是不是有些惊喜?实际上它是这样工作的:方法中的返回值只会简单返回方法中最后一行。在sayMoo
的例子中就意味着它会返回的是puts 'mooooooo…'*numberOfMoos
,也就是说返回值是nil
(因为puts
返回值是nil
)。如果我们想让所有的方法得到的返回值是字符串yellow submarine
,只需要在后面把我们想要的字符串加上就可以:
输出为:
mooooooo...mooooooo...
yellow submarine
那么现在我们就可以再回头看一下上一章中心理学调查的问题,但这次我们会写一个方法来帮我们问问题。在这个程序中会把想问的问题当成一个参数,如果回答是“yes”则返回true
,回答是“no”则返回false
(虽然我们大多数情况下是忽视答案的,但在方法中设置返回值还是很有必要的,同时我们在反馈尿床问题时也能用得上)。我会简略开头的欢迎部分和最后的收尾部分,方便大家阅读:
def ask question
goodAnswer = false
while (not goodAnswer)
puts question
reply = gets.chomp.downcase
if (reply == 'yes' or reply == 'no')
goodAnswer = true
if reply == 'yes'
answer = true
else
answer = false
end
else
puts 'Please answer "yes" or "no".'
end
end
answer # This is what we return (true or false).
end
puts 'Hello, and thank you for...'
puts
ask 'Do you like eating tacos?' # We ignore this return value.
ask 'Do you like eating burritos?'
wetsBed = ask 'Do you wet the bed?' # We save this return value.
ask 'Do you like eating chimichangas?'
ask 'Do you like eating sopapillas?'
ask 'Do you like eating tamales?'
puts 'Just a few more questions...'
ask 'Do you like drinking horchata?'
ask 'Do you like eating flautas?'
puts
puts 'DEBRIEFING:'
puts 'Thank you for...'
puts
puts wetsBed
程序运行得不错吧?现在我们可以更加方便得增加想问的问题了,同时我们的程序还更加短小精悍了!这的确是一个大的进步--让程序尽可能得短小精悍是每一个“懒”程序员的梦想。
摩拳擦掌
这里有个非常合适的英文数字转换的例子:输入一个数字(比如22)会输出相应的英文(这里指输出"twenty-two")。目前的话我们仅仅在0-100范围内的整数中测试。
(注意:这个例子中用另一种技巧return
来返回方法值,并且会介绍分支中一种新的转折elsif
。阅读代码中的前后文会清楚得理解它们的使用方法)
# We only want numbers from 0-100.
if number < 0
return 'Please enter a number zero or greater.'
end
if number > 100
return 'Please enter a number 100 or lesser.'
end
numString = '' # This is the string we will return.
# "left" is how much of the number we still have left to write out.
# "write" is the part we are writing out right now.
# write and left... get it? :)
left = number
write = left/100 # How many hundreds left to write out?
left = left - write*100 # Subtract off those hundreds.
if write > 0
return 'one hundred'
end
write = left/10 # How many tens left to write out?
left = left - write*10 # Subtract off those tens.
if write > 0
if write == 1 # Uh-oh...
# Since we can't write "tenty-two" instead of "twelve",
# we have to make a special exception for these.
if left == 0
numString = numString + 'ten'
elsif left == 1
numString = numString + 'eleven'
elsif left == 2
numString = numString + 'twelve'
elsif left == 3
numString = numString + 'thirteen'
elsif left == 4
numString = numString + 'fourteen'
elsif left == 5
numString = numString + 'fifteen'
elsif left == 6
numString = numString + 'sixteen'
elsif left == 7
numString = numString + 'seventeen'
elsif left == 8
numString = numString + 'eighteen'
elsif left == 9
numString = numString + 'nineteen'
end
# Since we took care of the digit in the ones place already,
# we have nothing left to write.
left = 0
elsif write == 2
numString = numString + 'twenty'
elsif write == 3
numString = numString + 'thirty'
elsif write == 4
numString = numString + 'forty'
elsif write == 5
numString = numString + 'fifty'
elsif write == 6
numString = numString + 'sixty'
elsif write == 7
numString = numString + 'seventy'
elsif write == 8
numString = numString + 'eighty'
elsif write == 9
numString = numString + 'ninety'
end
if left > 0
numString = numString + '-'
end
end
write = left # How many ones left to write out?
left = 0 # Subtract off those ones.
if write > 0
if write == 1
numString = numString + 'one'
elsif write == 2
numString = numString + 'two'
elsif write == 3
numString = numString + 'three'
elsif write == 4
numString = numString + 'four'
elsif write == 5
numString = numString + 'five'
elsif write == 6
numString = numString + 'six'
elsif write == 7
numString = numString + 'seven'
elsif write == 8
numString = numString + 'eight'
elsif write == 9
numString = numString + 'nine'
end
end
if numString == ''
# The only way "numString" could be empty is if
# "number" is 0.
return 'zero'
end
# If we got this far, then we had a number somewhere
# in between 0 and 100, so we need to return "numString".
numString
end
puts englishNumber( 0)
puts englishNumber( 9)
puts englishNumber( 10)
puts englishNumber( 11)
puts englishNumber( 17)
puts englishNumber( 32)
puts englishNumber( 88)
puts englishNumber( 99)
puts englishNumber(100)
输出为:
zero
nine
ten
eleven
seventeen
thirty-two
eighty-eight
ninety-nine
one hundred
程序先考虑100,后10-19,然后20、30、40~~90,再然后考虑中间数据,最终考虑数字0 --译者注
上面程序中有几点令人不太满意的:首先有太多重复部分,其次是没法解决100以上的大数,最后是程序里有太多特殊情况和返回值。其实我们可以用阵列方法来精简一下程序:
def englishNumber number
if number < 0 # No negative numbers.
return 'Please enter a number that isn\'t negative.'
end
if number == 0
return 'zero'
end
# No more special cases! No more returns!
numString = '' # This is the string we will return.
onesPlace = ['one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine']
tensPlace = ['ten', 'twenty', 'thirty', 'forty', 'fifty',
'sixty', 'seventy', 'eighty', 'ninety']
teenagers = ['eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
'sixteen', 'seventeen', 'eighteen', 'nineteen']
# "left" is how much of the number we still have left to write out.
# "write" is the part we are writing out right now.
# write and left... get it? :)
left = number
write = left/100 # How many hundreds left to write out?
left = left - write*100 # Subtract off those hundreds.
if write > 0
# Now here's a really sly trick:
hundreds = englishNumber write
numString = numString + hundreds + ' hundred'
# That's called "recursion". So what did I just do?
# I told this method to call itself, but with "write" instead of
# "number". Remember that "write" is (at the moment) the number of
# hundreds we have to write out. After we add "hundreds" to
# "numString", we add the string ' hundred' after it.
# So, for example, if we originally called englishNumber with
# 1999 (so "number" = 1999), then at this point "write" would
# be 19, and "left" would be 99. The laziest thing to do at this
# point is to have englishNumber write out the 'nineteen' for us,
# then we write out ' hundred', and then the rest of
# englishNumber writes out 'ninety-nine'.
if left > 0
# So we don't write 'two hundredfifty-one'...
numString = numString + ' '
end
end
write = left/10 # How many tens left to write out?
left = left - write*10 # Subtract off those tens.
if write > 0
if ((write == 1) and (left > 0))
# Since we can't write "tenty-two" instead of "twelve",
# we have to make a special exception for these.
numString = numString + teenagers[left-1]
# The "-1" is because teenagers[3] is 'fourteen', not 'thirteen'.
# Since we took care of the digit in the ones place already,
# we have nothing left to write.
left = 0
else
numString = numString + tensPlace[write-1]
# The "-1" is because tensPlace[3] is 'forty', not 'thirty'.
end
if left > 0
# So we don't write 'sixtyfour'...
numString = numString + '-'
end
end
write = left # How many ones left to write out?
left = 0 # Subtract off those ones.
if write > 0
numString = numString + onesPlace[write-1]
# The "-1" is because onesPlace[3] is 'four', not 'three'.
end
# Now we just return "numString"...
numString
end
puts englishNumber( 0)
puts englishNumber( 9)
puts englishNumber( 10)
puts englishNumber( 11)
puts englishNumber( 17)
puts englishNumber( 32)
puts englishNumber( 88)
puts englishNumber( 99)
puts englishNumber(100)
puts englishNumber(101)
puts englishNumber(234)
puts englishNumber(3211)
puts englishNumber(999999)
puts englishNumber(1000000000000)
程序在输出大于等于100的数据时使用了递归,调用自身方法 ----译者注
输出为:
zero
nine
ten
eleven
seventeen
thirty-two
eighty-eight
ninety-nine
one hundred
one hundred one
two hundred thirty-four
thirty-two hundred eleven
ninety-nine hundred ninety-nine hundred ninety-nine
one hundred hundred hundred hundred hundred hundred
嗯,程序显得好多了,代码相当紧凑,所以我在里面写了许多注释。程序可以用于更大的数……尽管不像我们希望的那样。例如我认为最后的数的输出是'one trrllion'更合适,或是'one million million'(当然三种都对),你现在就可以试着改一下……
牛刀小试
- 扩展一下上面的englishNumber程序:扩大到千位数字,使程序可以返回'one thousand'、'ten thousand'来替代'ten thousand'、'one hundred hundred'。
- 再扩展上面的程序:扩大到百万数字,使程序可以返回'one million'来替代'one thousand thousand',也可以试着增加到亿万和万亿。你最多能增加多少?
- 能不能将数字连接起来?与上面englishNumber程序类似,只是在数字中间加入‘and’,返回的是'nineteen hundred and seventy and two',或者是婚礼请柬上那样的数字。我本想多举几个例子,但我自己也不是太懂得这些。你可能需要联系婚礼人员来帮一下忙。
- "Ninety-nine bottles of beer..."现在你可以用上englishNumber和你之前的程序,重写一下输出这首歌的歌词的正确方法。惩罚一下你的电脑:让它从9999开始(虽然不要选一个太大的数字,因为让电脑在屏幕上输出如此多的内容需要挺多时间的)。输出十万个瓶子需要一点时间,如果你选择输出一百万个瓶子,你同时也是在惩罚你自己哦!
恭喜你!到现在为止,你就成为一个真正的程序员了!你已经学到了从头构建一个大程序的所有知识。如果你有想编写某个程序的想法,你就可以自己去写了,统统把它们解决掉吧!
当然从头构建所有的程序确实有点缓慢。有必要花费时间写别人已经写过的代码吗?你考虑过让程序发一封邮件吗?有没有想过让程序把网上的东西保存并下载到你的电脑上?或者是生成一个代码能够自动测试的网页教程?Ruby有多种多样的对象来帮助我们更加快速得写程序。