ruby way之Ranges

本文介绍了Ruby语言中区间的创建、操作及应用,详细解释了闭区间与开区间的概念,并通过实例展示了如何进行区间迭代、测试成员资格等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 1个简单的数字区间我们能这样表示:

[code] digits = 0..9
scale1 = 0..10
scale2 = 0...10[/code]

其中..包括结束点,...不包括结束点,也就是说digits和scale2的表示范围是一样的.

在ruby中,不只是数值可以用作区间,任何ruby对象都可以用作区间。可是并不是所有的表示都有意义或者有用。

接下来让我们来看看怎么操作区间:

2 开区间和闭区间

就像上面说的,..是闭区间,...是开区间:
[code]
r1 = 3..6 # closed
r2 = 3...6 # open
a1 = r1.to_a # [3,4,5,6]
a2 = r2.to_a # [3,4,5]
[/code]

我们没有任何方法来去除掉开始点,这是语言的限制,所以我们这只能说是半开区间.

3 得到终点

first和last方法返回一个区间的开始和结束点:

[code]r1 = 3..6
r2 = 3...6
r1a,r1b = r1.first,r1.last # 3, 6
r1c,r1d = r1.begin,r1.end # 3, 6
r2a,r2b = r2.begin,r2.end # 3, 6

puts r1a,r1b,r1c,r1d,r2a,r2b[/code]

这边要注意不论是开区间还是闭区间,last和end都会返回它本身的结束点.

exclude_end? 方法告诉我们是否一个区间是否是开的:

[code]r1.exclude_end? # false
r2.exclude_end? # true[/code]

4迭代一个区间

我们要是想要一个迭代成功,那么区间的元素必须定义了一个有意义的succ方法:

[code](3..6).each {|x| puts x } # prints four lines[/code]

这里要注意的是,当处理字符串的区间的时候。尽管String类定义了succ方法,可是他是一个受限制的。因为string的succ方法的定义并不是很严格,我们先看下面的例子:

[code]r1 = "7".."9"
r2 = "7".."10"
r1.each {|x| puts x } # Prints three lines
r2.each {|x| puts x } #什么都没有输出[/code]

r1,r2看起来没有什么不同,可是打印出的结果却是大相径庭。我们再仔细看,r2的结束点字符"10"的长度是2,和这个区间内的其他字符的长度是不一样的。

当我们尝试着迭代r2的时候,我们开始于字符"7",当下一个字符大于大于右边的结束点时,我们退出循环。但是“7”和“10”现在是字符而不是数字。他们之间的比较是按字典的顺序,然后我们就发现"7"比"10"大,因此r2就不会打印出任何东西.

这边要注意我们不能迭代一个浮点数的区间:

[code]fr = 2.0..2.2
fr.each {|x| puts x } # error![/code]

为什么浮点数没有succ方法呢?一个浮点数区间,每次增加一个浮点数字,只是理论上可行的。因为这个技术有很强的机器相关性,有时会在迭代一个很小的区间时,出现很大的数字,因此我们这样做了。

5 测试是否在区间内

我们使用include?方法来测试一个是否在区间:

[code]r1 = 23456..34567
x = 14142
y = 31416
r1.include?(x) # false
r1.include?(y) # true[/code]

member?方法是include?方法的别名。

include?方法内部是怎么样工作的呢,它是依据什么来判断的呢?在这里inculde?方法是依据<=>方法来判断的.

也就是说(a..b).include?(x) 可以写成这样: x >= a and x <= b.

再次说一下,要小心字符区间:

[code]s1 = "2".."5"
str = "28"
s1.include?(str) # true (misleading!)[/code]

6转换成一个数组

当我们转换一个区间到一个数组时,他只是简单的使用succ方法,然后将区间里的每一个元素放进一个数组:

[code]r = 3..12
arr = r.to_a # [3,4,5,6,7,8,9,10,11,12][/code]

他不能在浮点区间使用,字符区间有时可以使用,所以我们要避免这样做.

7 反向的区间

反向的区间也就是从大到小:
[code]r = 6..3
puts x = r.begin # 6
puts y = r.end # 3
puts flag = r.exclude_end? # false[/code]

反向区间也可以像一般区间那样操作:

[code]arr = r.to_a # []
r.each {|x| p x} # No iterations
y = 5
puts r.include?(y) # false (for any value of y)[/code]

反向区间最大的用途,是用在数组和字符中,因为在他们中左边的其实角标是0右边是-1:

[code]string = "flowery"
str1 = string[0..-2] # "flower"
str2 = string[1..-2] # "lower"
str3 = string[-5..-3] # "owe" (actually a forward range)[/code]


8flip-flop运算符

也就是把一个区间当作条件来进行操作。这个时候,这个区间就像是一个双态触发器。
这个的话,宝石书里面介绍的比较好,而且他还有图,大家可以看看那个就行了。

9自定义区间

[code]class Roman
include Comparable

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

Values = %w[M CM D CD C XC L XL X IX V IV I]

def Roman.encode(value)
return "" if self == 0
str = ""
Values.each do |letters|
rnum = const_get(letters)
if value >= rnum
return(letters + str=encode(value-rnum))
end
end
str
end

def Roman.decode(rvalue)
sum = 0
letters = rvalue.split('')
letters.each_with_index do |letter,i|
this = const_get(letter)
that = const_get(letters[i+1]) rescue 0
op = that > this ? :- : :+
sum = sum.send(op,this)
end
sum
end

def initialize(value)
case value
when String
@roman = value
@decimal = Roman.decode(@roman)
when Symbol
@roman = value.to_s
@decimal = Roman.decode(@roman)
when Numeric
@decimal = value
@roman = Roman.encode(@decimal)
end
end

def to_i
@decimal
end

def to_s
@roman
end

def succ
Roman.new(@decimal+1)
end

def <=>(other)
self.to_i <=> other.to_i
end
end[/code]

这是一个我们自定义的区间,代码很好懂,这边主要要看的就是我们定义的succ方法,当我们使用Roman对象作为区间的时候,我们如果迭代他,就会自动调用succ方法,下面来看测试代码:

[code]require 'roman'

y1 = Roman(:MCMLXVI)
y2 = Roman(:MMIX)
range = y1..y2 # 1966..2009
range.each {|x| puts x} # 44 lines of output

epoch = Roman(:MCMLXX)
range.include?(epoch) # true

doomsday = Roman(2038)
range.include?(doomsday) # false

Roman(:V) == Roman(:IV).succ # true
Roman(:MCM) < Roman(:MM) # true[/code]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值