《Learn To Program》是一本初学者编程入门书,作者通过 Ruby 作为教学语言,以精炼却生动的例子带你进入编程的世界。
此书即是初学者编程入门教程,也是Ruby的入门教程。因此对于有一定编程基础并想学习Ruby的也在这本书上获得不菲的知识。
全书共分12课,这里将他们列出来并加入了链接,感兴趣的可以自行前往阅读。
- Getting Started
- Numbers
- Letters
- Variables and Assignment
- Mixing It Up
- More About Methods
- Flow Control
- Arrays and Iterators
- Writing Your Own Methods
- Classes
- Blocks and Procs
- Beyond This Tutorial
既然以Ruby作为本书的教程语言,那么环境安装自然不再话下。课程1就是教你如何安装Ruby,并且推荐使用一款趁手的IDE来编写Ruby。笔者使用VSCode来编写Ruby,个人感觉还不赖。在初学阶段,笔者建议可以直接通过irb来学习或调试Ruby,直接在终端输入irb试试吧。
Numbers
Ruby中的Number类型,有Integer和Float(注意Ruby中没有int、long之分,也没有float、double之分,只有两种数字类型,Integer和Float。)。以及Number之间的逻辑运算+-*/。
练习下下面的打印,看看是不是和想的一样。
puts 1+2
puts 2*3
puts 5-8
puts 9/2
3
6
-3
4
Letters
在Ruby中,字符串的表示以一对‘’或者""来表示。
puts 'Hello, world!'
puts ''
puts "Good-bye."
而字符串的拼接可以简单的通过+来表示。
puts 'I like' + ' apple pie.'
I like apple pie.
拼接重复字符串,我们还可以借助于*运算符。
puts 'blink ' * 4
blink blink blink blink
当然以上字符串和数字的组合是因为不管是+还是*,都是String的一个函数,因此,我们才可以像调用函数那样使用他们。
我们也可以按如下方式调用:
puts 'I like'.+(' apple pie.')
I like apple pie.
他们的伪代码如下:
class String
def +(other)
"#{self}#{other}"
end
def *(other)
s = self
other.times do
s = s + self
end
return s
end
end
Variables and Assignment
这一课讲的是变量的赋值取值,以一个例子说明:(注意,Ruby的变量是不需要指定类型的,这是一门动态语言,会根据赋值来改变变量的类型,非常灵活)
composer = 'Mozart'
puts composer + ' was "da bomb", in his day.'
Mixing It Up
当我们运行下面的例子时程序就会提示我们语法错误。这是为什么呢?类型不匹配了,所以在运行时报错。数字和字符串怎么能加在一起,只有数字和数字,字符串和字符串之间才能相加。
var1 = 2
var2 = '5'
puts var1 + var2
这就需要Number和String之间能够相互转换。
to_s表示转换成字符串。因此下面的表达式成立。
var1 = 2
var2 = '5'
puts var1.to_s + var2
其他还有to_i转换成整型,to_f转换成浮点型。
puts '15'.to_f
puts "10".to_i
More About Methods
Ruby以.语法来调用函,以字符串为例。
var1 = 'stop'
puts var1.reverse
pots
通过调用gets函数来获取用户输入。gets.chomp在用户输入后自动换行。
puts 'What is your full name?'
name = gets.chomp
puts 'Did you know there are ' + name.length.to_s +
' characters in your name, ' + name + '?'
What is your full name?
User Input
Did you know there are 22 characters in your name, Christopher David Pine?
# 5的2次方
puts 5**2
# 取余
puts 7%3
# 绝对值
puts((5-2).abs)
puts((2-5).abs)
# 不大于1的随机数(浮点型)
puts rand
# 0<=x<10的随机整数
puts rand(10)
Flow Control
控制流,也就是比较>、<、=>、==、<=、!、!=
puts 1 == 1
puts 2 != 1
puts 1 > 2
true
true
false
条件分支if、else、elif、end
puts 'Hello, what\'s your name?'
name = gets.chomp
puts 'Hello, ' + name + '.'
if name == 'Chris'
puts 'What a lovely name!'
end
Hello, what's your name?
Your input...
Hello, Chris.
What a lovely name!
循环语句while
command = ''
while command != 'bye'
puts command
command = gets.chomp
end
puts 'Come again soon!'
Hello?
Hello?
Hi!
Hi!
Very nice to meet you.
Very nice to meet you.
Oh... how sweet!
Oh... how sweet!
bye
Come again soon!
逻辑判断合并or(||)、and(&&)、not(!)
puts 'Hello, what\'s your name?'
name = gets.chomp
puts 'Hello, ' + name + '.'
if (name == 'Chris' or name == 'Katy')
puts 'What a lovely name!'
elsif name == 'Jack' and name.length == 4
puts ""
end
Hello, what's your name?
Katy
Hello, Katy.
What a lovely name!
Arrays and Iterators
Ruby中表示数组的方式为array = [1,2,3,"3"],其中数组中每个元素的类型都可以不一样。
获取数组内元素
puts array[0]
puts array[1]
puts array[-1]
1
2
"3"
要获取数组中的每一个元素,可以通过each方法遍历。
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 '...'
I love English!
Don't you?
I love German!
Don't you?
I love Ruby!
Don't you?
And let's hear it for C++!
...
而要重复操作一定次数,可以通过integer的times放啊实现。
3.times do
puts 'Hip-Hip-Hooray!'
end
Hip-Hip-Hooray!
Hip-Hip-Hooray!
Hip-Hip-Hooray!
Writing Your Own Methods
在ruby中定义方法的格式为 ,参数可省
def sayMoo numberOfMoos
puts 'mooooooo...'*numberOfMoos
end
sayMoo 3
puts 'oink-oink'
sayMoo
mooooooo...mooooooo...mooooooo...
oink-oink
Classes
ruby使用关键字class来定义类,当这个类是已有的类时,表示往这个类中新增信息。
class Integer
def to_eng
if self == 5
english = 'five'
else
english = 'fifty-eight'
end
english
end
end
puts 5.to_eng
puts 58.to_eng
five
fifty-eight
类的实例变量通过 @+变量名表示,类变量则是@@+变量名
class Die
def roll
@numberShowing = 1 + rand(6)
end
def showing
@numberShowing
end
end
die = Die.new
die.roll
puts die.showing
puts die.showing
die.roll
puts die.showing
puts die.showing
4
4
6
6
Blocks and Procs
这一课讲Ruby中的闭包,可以理解成一个代码快,写在do end之间,用Proc类表示。do end也可以写成{}形式。闭包可以作为函数的参数使用,也可以直接像函数那样使用。
# 创建了一个闭包,存到变量toast中,调用call来执行闭包中的代码块
toast = Proc.new do
puts 'Cheers!'
end
toast.call
toast.call
toast.call
Cheers!
Cheers!
Cheers!
闭包中还可以传入参数使用。
doYouLike = Proc.new do |aGoodThing|
puts 'I *really* like '+aGoodThing+'!'
end
doYouLike.call 'chocolate'
doYouLike.call 'ruby'
I really like chocolate!
I really like ruby!
当闭包作为函数参数时:
def doSelfImportantly someProc
puts 'Everybody just HOLD ON! I have something to do...'
someProc.call
puts 'Ok everyone, I\'m done. Go on with what you were doing.'
end
sayHello = Proc.new do
puts 'hello'
end
sayGoodbye = Proc.new do
puts 'goodbye'
end
doSelfImportantly sayHello
doSelfImportantly sayGoodbye
Everybody just HOLD ON! I have something to do...
hello
Ok everyone, I'm done. Go on with what you were doing.
Everybody just HOLD ON! I have something to do...
goodbye
Ok everyone, I'm done. Go on with what you were doing.
当闭包作为函数的返回值时:
def compose proc1, proc2
Proc.new do |x|
proc2.call(proc1.call(x))
end
end
squareIt = Proc.new do |x|
x * x
end
doubleIt = Proc.new do |x|
x + x
end
doubleThenSquare = compose doubleIt, squareIt
squareThenDouble = compose squareIt, doubleIt
puts doubleThenSquare.call(5)
puts squareThenDouble.call(5)
100
50
我们还可以不以Proc的方式将闭包传入函数中,只要在参数前面加&.
class Array
# 如果像前面那样直接传入Proc对象,那么在调用时就不能直接在方法后面根do end的函数块,需要先创建Proc的对象再传入才能正常运行。
def eachEven(&wasABlock_nowAProc)
# We start with "true" because arrays start with 0, which is even.
isEven = true
self.each do |object|
if isEven
wasABlock_nowAProc.call object
end
isEven = (not isEven) # Toggle from even to odd, or odd to even.
end
end
end
['apple', 'bad apple', 'cherry', 'durian'].eachEven do |fruit|
puts 'Yum! I just love '+fruit+' pies, don\'t you?'
end
# Remember, we are getting the even-numbered elements
# of the array, all of which happen to be odd numbers,
# just because I like to cause problems like that.
[1, 2, 3, 4, 5].eachEven do |oddBall|
puts oddBall.to_s+' is NOT an even number!'
end
Yum! I just love apple pies, don't you?
Yum! I just love cherry pies, don't you?
1 is NOT an even number!
3 is NOT an even number!
5 is NOT an even number!
多参数时,block只能放在最后面
def profile descriptionOfBlock, &block
startTime = Time.now
block.call
duration = Time.now - startTime
puts descriptionOfBlock+': '+duration.to_s+' seconds'
end
profile '25000 doublings' do
number = 1
25000.times do
number = number + number
end
# Show the number of digits in this HUGE number.
puts number.to_s.length.to_s+' digits'
end
profile 'count to a million' do
number = 0
1000000.times do
number = number + 1
end
end
7526 digits
25000 doublings: 0.026852 seconds
count to a million: 0.039258 seconds
接下来我们来做一个随书小练习。
part1: Grandfather Clock. Write a method which takes a block and calls it once for each hour that has passed today. That way, if I were to pass in the block do puts 'DONG!' end, it would chime (sort of) like a grandfather clock. Test your method out with a few different blocks (including the one I just gave you). Hint: You can use Time.now.hour to get the current hour. However, this returns a number between 0 and 23, so you will have to alter those numbers in order to get ordinary clock-face numbers (1 to 12).
意思就是我在1~12小时之间,每过一小时就打印一次‘DONG!’
def onHourAlarm(&block)
hour = Time.new.hour
if hour > 12
hour = hour - 12
end
block.call hour
end
onHourAlarm do |hour|
puts "It is #{hour} o 'clock"
end
我们还可以隐式的调用函数的block,像下面这样:
def doItTwice
if block_given?
yield
yield
else
puts 'no block'
end
end
doItTwice do
puts 'buritiate mustripe lablic acticise'
end
buritiate mustripe lablic acticise
buritiate mustripe lablic acticise
yield关键字表示隐式调用函数传入的闭包,block_given?就是判断这个函数是否有传入闭包。