网安
  • Develop
    • JAVA学习
      • 字节码
      • API开发
      • Web开发
      • 工程结构推荐
      • 创建第一个项目
      • 权限管控
      • 配置文件
      • 日志管理
      • 数据访问
      • 性能监控
      • IoC容器
      • Spring
      • Filter与Listener
      • jsp
      • MVC
      • servlet-1
      • servlet-2
      • servlet-3
      • servlet-4
      • FreeMarker
      • Thymeleaf
      • EL
      • SpEL
      • JSTL
      • 部署
      • JDBC
      • 数据库连接池
      • fastjson
      • jackson
      • XML
      • JSON
      • 序列化
      • Maven
      • 安装与使用
      • 工具
      • 爬虫
    • GO学习
      • GO
        • flag 包
        • goland 远程调试
        • GoReleaser
        • OS 包
        • time 包
        • 格式化输出
    • Lua学习
      • Lua
      • 基础语法
      • LuaJIT
      • 与系统交互
    • Pyhon
      • 基础
      • Django
      • CLI
      • miniforge
      • MockingBird
      • pdb
      • pyc
      • 装的我脑血栓要犯了
      • Python101
      • 反序列化
      • 爬虫
      • Pillow
      • 图像识别
      • flask
    • Speed-Ruby
      • 入门1
      • 入门2 对象
      • 入门3 创建命令
      • Encoding类
      • File类与Dir类
      • IO
      • Proc类
      • Time类与Date类
      • 正则
      • 错误处理与异常
      • 对象、变量和常量
      • 方法
      • 数值
      • 数组
      • 条件判断
      • 循环
      • 运算符
      • Socket编程
      • 字符串
      • 并发与线程
      • 块
      • 类和模块
      • 散列
    • Web
      • HTTP
        • Connection
        • HTTP 报文
        • Vary
      • 笔记
        • 跳转
        • 认证 & 授权
        • 同源策略(SOP)
        • 文件
    • Git 学习笔记
    • JSON
      • JSON 学习笔记
    • HTML
      • Speed-HTML
      • 语法学习
      • HTML字符实体
    • XML
      • XML 学习笔记
    • 计算机基础
      • 操作系统
      • 计算机组成
      • 算法
      • 内存
      • 字符编码
    • gnuplot 学习笔记
    • regex
  • Integrated
    • Linux
      • God-Linux
      • Secure-Linux
      • Power-Linux
      • IO模型
      • Speed-Linux
      • 发行版
      • 工具
      • 启动过程
      • 进程
      • 认证
      • 日志
      • 守护进程
      • 文件
      • 信息
      • VSFTP 配置案例
      • auditd
      • containerd
      • DNS 配置案例
      • Docker
      • Docker-Compose
      • firewalld 实验
      • gpg
      • Iptables
      • httpd
      • LAMP
      • mysql
      • nfs 配置案例
      • openssl
      • PAM
      • samba 配置案例
      • terraform
      • ufw
      • VSFTP 配置案例
    • Network
      • Speed-Net
      • Power-Net
      • SDN 笔记
      • DNS
      • TLS
    • Windows
      • Secure-Win
      • Speed-Win
      • ACL
      • LDAP
      • IPC$(Internet Process Connection)
      • PDB符号文件
      • 工作组
      • WinRM
      • 角色权限
      • 凭据
      • 签名
      • 日志
      • 认证
      • 协议
      • 信息
      • 应用
      • 组策略
      • 域
      • asp站点搭建
      • Exchange 搭建
      • Windows 故障转移集群
      • Windows 基础服务搭建
      • Windows 域搭建
      • 本地抓包
      • PowerShell 笔记
    • 容器
      • Docker
    • 数据库
      • Speed-SQL
      • Power-SQL
      • MSSQL
      • MySQL
      • Postgresql
      • Redis
      • MySQL大小写问题
      • 主键和外键
      • MySQL快速入门
      • 虚拟化
        • ESXi
        • vCenter
  • Plan
    • Mac-Plan
    • Misc-Plan
    • Team-Plan
    • Thinking-Plan
    • VM-Plan
  • Sercurity
    • Power-PenTest
    • BlueTeam
      • 安全建设
      • 分析
      • 加固
      • 取证
      • 应急
      • USB取证
      • 磁盘取证
      • 内存取证
      • ClamAV 部署
      • yara 实验
      • 安防设施搭建使用
      • ZIP明文攻击
      • 流量分析
    • Crypto
      • Crypto
        • 2020 9 G60攻防大赛
        • CTF
        • 2020 9 中能融合杯工控CTF
        • 2020 10 全国工业互联网安全技术技能大赛江苏省选拔赛
        • 2020 10 全国网络与信息安全管理职业技能大赛江苏场
        • 2020 11 I²S峰会暨工业互联网安全大赛
        • 2021 6 第二届I²S峰会暨工业互联网安全大赛
        • 2021-9-第七届工控信息安全攻防竞赛
        • 2021 9 第七届全国职工职业技能大赛某市县选拔赛
        • 2021 9 全国网络与信息安全管理职业技能大赛江苏场
        • 2021-10-G60攻防大赛
    • CTF
      • CTF
      • writeup
        • 2020 9 中能融合杯工控CTF
        • 2020 9 G60攻防大赛
        • 2020 10 全国工业互联网安全技术技能大赛江苏省选拔赛
        • 2020 10 全国网络与信息安全管理职业技能大赛江苏场
        • 2020 11 I²S峰会暨工业互联网安全大赛
        • 2021 6 第二届I²S峰会暨工业互联网安全大赛
        • 2021-9-第七届工控信息安全攻防竞赛
        • 2021 9 第七届全国职工职业技能大赛某市县选拔赛
        • 2021 9 全国网络与信息安全管理职业技能大赛江苏场
        • 2021-10-G60攻防大赛
    • ICS
      • PLC攻击
      • S7comm 相关
      • 工控协议
      • 上位机安全
      • Modbus 仿真环境搭建
      • siemens 仿真搭建实验
      • S7-300 启停实验
    • IOT
      • 无线电安全
        • RFID复制卡
        • RFID基础知识
        • WiFikiller
      • 硬件安全
        • DIY键盘嵌入指纹识别模块实验记录
        • Device-Exploits
        • HID-Digispark
        • HID-KeyboardLogger
        • HID-USBHarpoon
        • HID-USBKeyLogger
      • 固件安全
        • 固件安全
        • Dlink_DWR-932B 路由器固件分析
    • Mobile sec
      • 小程序安全
      • Android安全
    • PWN
      • SLMail溢出案例
      • PWN
    • Red Team
      • OS安全
        • Linux 安全
        • Exploits
        • NTLM中继
        • Windows 安全
        • Responder欺骗
        • Windows-LOL
      • Web_Generic
        • Top 10
          • RCE
          • Fileread
          • SQLi
          • SSRF
          • SSTI
          • Web Generic
          • XSS
          • XXE
      • Web_Tricks
        • JWT 安全
        • HTTP_request_smuggling
        • OOB
        • 绕过访问
      • 靶场
        • Hello-Java-Sec 学习
        • DVWA-WalkThrough
        • pikachu-WalkThrough
        • upload-labs-WalkThrough
        • XVWA-WalkThrough
        • XSS挑战-WalkThrough
      • 实验
        • flask
        • fastjson
        • Log4j
        • nodejs
        • Shiro
        • Spring
        • Weblogic
      • 前端攻防
      • IDOR
    • 安防设备
      • Exploits
      • Bypass 技巧
    • 后渗透
      • 权限提升
      • 后渗透
      • 权限维持
      • 实验
        • C2 实验
        • Exchange
        • 端口转发实验
        • 代理实验
        • 免杀实验
        • 隧道实验
    • 软件服务安全
      • Exploits
      • CS Exploits
      • 实验
        • Docker
        • Kubernetes
        • Mysql
        • Oracle
        • PostgreSQL
        • Redis
        • vCenter
    • 协议安全
      • Exploits
    • 信息收集
      • 端口安全
      • 空间测绘
      • 信息收集
    • 语言安全
      • 语言安全
        • 语言安全
      • GO安全
        • GO安全
        • Go代码审计
      • JAVA安全
        • JAVA安全
        • JAVA代码审计
        • JAVA反序列化
        • SpEL 注入
      • PHP安全
        • PHP安全
        • bypass_disable_function
        • bypass_open_basedir
        • phpinfo
        • PHP代码审计
        • PHP反序列化
        • PHP回调函数
        • 变量覆盖
        • POP
        • 弱类型
        • 伪协议
        • 无字母数字Webshell
      • Python安全
        • pyc反编译
        • Python安全
        • Python 代码审计
        • 沙箱逃逸
      • dotnet安全
      • JS安全
    • 云安全
      • 公有云安全
    • Reverse
      • Reverse
      • FILE
        • ELF
        • BMP
        • JPG
        • PE
        • PNG
        • ZIP
        • 文件头
      • 实验
        • PYAble
          • 2-逆运算
          • 1-基本分析
          • 3-异或
          • 4-Base64
          • 5-Base64换表
          • 6-动态调试
        • Windows
          • condrv.sys 内存损坏漏洞
    • 工具
      • Aircrack
      • BloodHound
      • Burp Suite
      • frp
      • CobaltStrike
      • Ghidra
      • fscan
      • Hashcat
      • IDA
      • merlin
      • Kali
      • Metasploit
      • Mimikatz
      • ModSecurity
      • Nmap
      • nps
      • nuclei
      • pupy
      • RedGuard
      • SET
      • sliver
      • Snort
      • Sqlmap
      • Suricata
      • Sysmon
      • uncover
      • Volatility
      • Wfuzz
      • Wireshark
      • xray
    • 安全资源
      • 靶机
        • VulnHub
          • DC
            • DC2 WalkThrough
            • DC1 WalkThrough
            • DC3 WalkThrough
            • DC4 WalkThrough
            • DC5 WalkThrough
            • DC6 WalkThrough
            • DC9 WalkThrough
            • DC8 WalkThrough
          • It's_October
            • It’s_October1 WalkThrough
          • Kioptrix
            • Kioptrix2 WalkThrough
            • Kioptrix3 WalkThrough
            • Kioptrix4 WalkThrough
            • Kioptrix5 WalkThrough
          • Mission-Pumpkin
            • PumpkinGarden-WalkThrough
            • PumpkinFestival WalkThrough
            • PumpkinRaising WalkThrough
          • Symfonos
            • symfonos1 WalkThrough
            • symfonos2 WalkThrough
            • symfonos3 WalkThrough
            • symfonos5 WalkThrough
        • Wargames
          • Bandit
            • Bandit-WalkThrough
      • 面试问题
        • 面试问题
Powered by GitBook
On this page
  • 数值类
  • 数值类的构成
  • 数值的字面量
  • 算数运算
  • Math 模块
  • 数值类型转换
  • 位运算
  • 随机数
  • 计数
  • 近似值误差
  1. Develop
  2. Speed-Ruby

数值

数值类


  • https://www.kancloud.cn/imxieke/ruby-base/107300


数值类的构成

在数值类中,有像 -1、0、1、10 这样的表示整数的 Integer 类,也有像 0.1、3.141592 这样的具有精度的、表示浮点小数的 Float 类。

这些数值类都被定义为了 Numeric 类的子类。另外,Integer 类又可以分为两种,一种是表示计算机硬件可以处理的数值的 Fixnum 类,另外一种是表示比 Fixnum 更大的数值的 Bignum 类。

程序中用到的整数一般都是 Fixnum 类范围内的整数。如果使用的整数超过了 Fixnum 的范围,Ruby 就会自动将其转换为 Bignum 类。因此,在写程序的时候,我们几乎可以忽略上述整数类的区别。下面是计算 2 的 10 次幂以及 2 的 1000 次幂的例子,** 是表示乘方的运算符。

执行示例

> irb --simple-prompt
>> n = 2 ** 10
=> 1024
>> n.class
=> Fixnum
>> m = 2 ** 1000
=> 1071508607186267320948425049060001810561404811705533607443750388370351051124936
1224931983788156958581275946729175531468251871452856923140435984577574698574803934
5677748242309854210746050623711418779541821530464749835819412673987675591655439460
77062914571196477686542167660429831652624386837205668069376
>> m.class
=> Bignum

Ruby 也可以处理有理数和复数。表示有理数用 Rational 类,表示复数用 Complex 类。

Rational 对象用“Rational( 分子 , 分母 )”的形式定义

上述这样的分数计算,可以用 Rational 对象改写成下面那样。我们还可以使用 Rational.to_f 方法将其转换为 Float 对象。

a = Rational(2, 5)
b = Rational(1, 3)
p a                #=> (2/5)
p b                #=> (1/3)
c = a + b
p c                #=> (11/15)
p c.to_f           #=> 0.7333333333333333

Complex 对象用“Complex( 实数 , 虚数 )”的形式定义。以下是计算复数 2i 的 2 次幂的例子:

c = Complex(0, 2) ** 2
p c                    #=> (-4+0i)

数值的字面量

数值对象的字面量

字面量 | 作用(括号内为 10 进制的值)

  • | - 123 | 表示10 进制整数 0123 | 表示8 进制整数(83) 0o123 | 表示8 进制整数(83) 0d123 | 表示10 进制整数(123) 0x123 | 表示16 进制整数(291) 0b1111011 | 表示2 进制整数(123) 123.45 | 浮点小数 1.23e4 | 浮点小数的指数表示法(1.23×10 的4 次幂=12300.0) 1.23e-4 | 浮点小数的指数表示法(1.23×10 的-4 次幂=0.000123)

单纯的数字罗列表示 10 进制整数。以 0b 开头的数值表示 2 进制数,以 0 或者 0o 开头的数值表示 8 进制数,以 0d 开头的数值表示 10 进制数,以 0x 开头的数值表示 16 进制数。字面量中的 _ 会被自动忽略。因此,在使用每 3 位数字间隔一下这样的数值表示方法时会十分方便。

p 1234567         #=> 1234567
p 1_234_567       #=> 1234567
p 0b11111111      #=> 255
p 01234567        #=> 342391
p 0x12345678      #=> 305419896

包含小数点的数值为浮点小数。我们还可以采用有效数字与指数配合的科学计数法来表示浮点小数。格式为“有效数字”+“英文字母 e(或者 E)”+“表示指数的整数”。

p 1.234         #=> 1.234
p 1.234e4       #=> 12340.0
p 1234e-4       #=> 0.0001234

算数运算

算数运算的运算符

运算符 | 运算

  • | -

  •   | 加法运算
  •   | 减法运算
  •   | 乘法运算

/ | 除法运算 % | 取余运算 ** | 乘方运算

Integer 对象与 Float 对象的运算结果为 Float 对象。

Integer 对象之间、Float 对象之间的运算结果分别为 Integer 对象、Float 对象。

p 1 + 1        #=> 2
p 1 + 1.0      #=> 2.0
p 2 - 1        #=> 1
p 2 - 1.0      #=> 1.0
p 3 * 2        #=> 6
p 3 * 2.0      #=> 6.0
p 3 * -2.0     #=> -6,0
p 5 / 2        #=> 2
p 5 / 2.0      #=> 2.5
p 5 % 2        #=> 1
p 5 % 2.0      #=> 1.0
p 5 ** 2       #=> 25
p 5 ** 0.5     #=> 2.23606797749979
p 5 ** -2.0    #=> 0.04
p 5 ** -2      #=> 0.04

这里需要注意的是,指数为负整数的乘方返回的结果是表示有理数的 Rational 对象。

p 5 ** -2.0    #=> 0.04
p 5 ** -2      #=> (1/25)

除法

除了 / 和 % 以外,数值对象中还有一些与除法相关的方法。

  • x.div(y)

    返回 x 除以 y 后的商的整数。

    p 5.div(2)        #=> 2
    p 5.div(2.2)      #=> 2
    p -5.div(2)       #=> -3
    p -5.div(2.2)     #=> -3
  • x.quo(y)

    返回 x 除以 y 后的商,如果 x、y 都是整数则返回 Rational 对象。

    p 5.quo(2)        #=> (5/2)
    p 5.quo(2.2)      #=> 2.2727272727272725
    p -5.quo(2)       #=> (-5/2)
    p -5.quo(2.2)     #=> -2.2727272727272725
  • x.modulo(y)

    与 x % y 等价。

  • x.divmod(y)

    将 x 除以 y 后的商和余数作为数组返回。商是将 x / y 的结果去掉小数点后的部分而得到的值。余数的符号与 y 的符号一致,余数的值为 x % y 的结果。假设有运算式如下,

    ans=x.divmod(y)

    这时,下面的等式是成立的。

    x==ans[0] *y + ans[1]
    
    p 10.divmod(3.5)        #=> [2, 3.0]
    p 10.divmod(-3.5)       #=> [-3, -0.5]
    p -10.divmod(3.5)       #=> [-3, 0.5]
    p -10.divmod(-3.5)      #=> [2, -3.0]
  • x.remainder(y)

    返回 x 除以 y 的余数,结果的符号与 x 的符号一致。

    p 10.remainder(3.5)        #=> 3.0
    p 10.remainder(-3.5)       #=> 3.0
    p -10.remainder(3.5)       #=> -3.0
    p -10.remainder(-3.5)      #=> -3.0

    另外,除数为 0 时,Integer 类会返回错误,而 Float 类则会返回 Infinity(无限大)或者 NaN(Not a Number)。如果再用这两个值进行运算,那么结果只会返回 Infinity 或者 NaN。程序把输入的数据直接用于运算的时候,除数有可能会为 0,我们应当注意避免这样的情况发生。

    p 1 / 0         #=> 错误(ZeroDivisionError)
    p 1 / 0.0       #=> Infinity
    p 0 / 0.0       #=> NaN
    p 1.divmod(0)   #=> 错误(ZeroDivisionError)
    p 1.divmod(0.0) #=> 错误(FloatDomainError)

Math 模块

Math 模块提供了三角函数、对数函数等常用的函数运算的方法。该模块中定义了模块函数和常量,例如,求平方根时,可以采用下述方法。

p Math.sqrt(2)    #=> 1.4142135623730951

Math 模块定义的方法

方法名 | 作用

  • | - acos(x) | 反余弦函数 acosh(x) | 反双曲余弦函数 asin(x) | 反正弦函数 asinh(x) | 反双曲正弦函数 atan(x) | 反正切函数 atan2(x, y) | 表示 4 个象限的反正切函数 atanh(x) | 反双曲正切函数 cbrt(x) | 立方根 cos(x) | 余弦函数 cosh(x) | 双曲余弦函数 erf(x) | 误差函数 erfc(x) | 余补误差函数 exp(x) | 指数函数 frexp(x) | 把一个浮点数分解为尾数和指数 gamma(x) | 伽玛函数 hypot(x, y) | 计算三角形的斜边长度 ldexp(x, y) | 返回 x 乘以 2 的 y 次幂的值 lgamma(x) | 伽马函数的自然对数 log(x) | 底数为 e 的对数(自然对数) log10(x) | 底数为 10 的对数(常用对数) log2(x) | 底数为 2 的对数 sin(x) | 正弦函数 sinh(x) | 双曲正弦函数 sqrt(x) | 平方根 tan(x) | 正切函数 tanh(x) | 双曲正切函数

Math 模块定义的常量

常量名 | 作用

  • | - PI | 圆周率(3.141592653589793) E | 自然对数的底数(2.718281828459045)

数值类型转换

将 Integer 对象转换为 Float 对象时,可以使用 to_f 方法。相反,使用 to_i 方法则可以将 Float 对象转换为 Integer 对象(Integer.to_i 方法和 Float.to_f 方法返回与接收者一样的值)。另外,也可以把字符串转换为数值。

p 10.to_f       #=> 10.0
p 10.8.to_i     #=> 10
p -10.8.to_i    #=> -10
p "123".to_i    #=> 123
p "12.3".to_f   #=> 12.3

Float.to_i 方法返回的结果会把小数点以后的值去掉。我们用 round 方法对小数进行四舍五入的处理。

p 1.2.round    #=> 1
p 1.8.round    #=> 2
p -1.2.round   #=> -1
p -1.8.round   #=> -2

返回比接收者大的最小整数用 ceil 方法,返回比接收者小的最大整数用 floor 方法。

p 1.5.ceil     #=> 2
p -1.5.ceil    #=> -1
p 1.5.floor    #=> 1
p -1.5.floor   #=> -2

我们还可以将数值转换为 Rational 对象和 Complex 对象,分别使用 to_r 和 to_c 方法,如下所示。

p 1.5.to_r     #=> (3/2)
p 1.5.to_c     #=> (1.5+0i)

位运算

Integer 类的位运算符

运算符 | 运算

  •   | -

~ | 按位取反(一元运算符) & | 按位与 | | 按位或 ^ | 按位异或 ((a&~b|~a&b)) >> | 位右移 << | 位左移

def pb(i)
  # 使用printf 的%b 格式
  # 将整数的末尾8 位用2 进制表示
  printf("%08b\n", i & 0b11111111)
end

b = 0b11110000
pb(b)              #=> 11110000
pb(~b)             #=> 00001111
pb(b & 0b00010001) #=> 00010000
pb(b | 0b00010001) #=> 11110001
pb(b ^ 0b00010001) #=> 11100001
pb(b >> 3)         #=> 00011110
pb(b << 3)         #=> 10000000

随机数

有时候随机性可能会帮助我们解决一些问题。随机性一般有以下特质。

  • 没有规则和法则依据

  • 一定范围内的数会均等地出现

    拿掷骰子为例,我们不能预测下一个投出的是哪一面,但骰子各个面投出的几率都是一样的。我们把这样的情况称为随机,随机得到的数值称为随机数。在掷骰子或者洗扑克牌那样需要偶然性的情况下,或者像加密后的密码那样希望得到一些难以被预测的数据时,一般都会用到随机数。

    我们可以用 Random.rand 方法得到随机数。不指定参数时,Random.rand 方法返回比 1 小的浮点小数。参数为正整数时,返回 0 到该正整数之间的数值。

    p Random.rand        #=> 0.13520495197709
    p Random.rand(100)   #=> 31
    p Random.rand(100)   #=> 84

    程序不能生成真正的随机数,只能通过某种算法生成看起来像随机数的值,这样的随机数称为模拟随机数。生成模拟随机数需要以某个值为基础,这个值称为随机数的种子。模拟随机数终究只是通过计算得到的数值,只要随机数的种子一样,那么得到值就有可能重复出现。使用 Random.new 方法初始化随机数生成器,然后再使用 Random.rand 方法,就可以对 Random 对象指定随机数种子,从而生成随机数。

    r1 = Random.new(1)    # 初始化随机数组
    p [r1.rand, r1.rand]
    #=> [0.417022004702574, 0.7203244934421581]
    
    r2 = Random.new(1)    # 再次初始化随机数组
    p [r2.rand, r2.rand]
    #=> [0.417022003702574, 0.7203244934421581]

    Random.new 方法不指定参数的情况下,则会用随机生成的随机数种子初始化 Random 对象,因此每次得到的随机数组也会不一样。

    r1 = Random.new
    p [r1.rand, r1.rand]
    #=> [0.49452535392946817, 0.34141702823098863]
    
    r2 = Random.new
    p [r2.rand, r2.rand]
    #=> [0.9464262066747281, 0.01911968591048996]

    在信息安全领域中,“优质的随机”是一个重要的课题。生成用于加密 key 的随机数时,不能重复出现是非常重要的,因此就需要我们慎重地选择难以被预测的随机种子。在一些特殊的情况下可能会需要初始化 Random 对象,而一般情况下直接用最开始介绍的 Random.rand 方法就足够了。

计数

除了数值计算外,Integer 类还能计算处理的次数、数组的元素个数等。接下来介绍的方法就是按照数值指定的次数执行循环处理的迭代器。

  • n.times{|i| … }

    循环 n 次,从 0 到 n-1 的值会被依次赋值给块变量。

    ary = []
    10.times do |i|
    ary << i
    end
    p ary    #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • from.upto(to){|i| … }

    从 from 开始循环对 i 进行加 1 处理,直到 i 等于 to。from 比 to 大时不会执行循环处理。

    ary = []
    2.upto(10) do |i|
    ary << i
    end
    p ary    #=> [2, 3, 4, 5, 6, 7, 8, 9, 10]
  • from.downto(to){…}

    从 from 开始循环对 i 进行减 1 处理,直到 i 等于 to。from 比 to 小时不会执行循环处理。

    ary = []
    10.downto(2) do |i|
    ary << i
    end
    p ary    #=> [10, 9, 8, 7, 6, 5, 4, 3, 2]
  • from.step(to, step){…}

    从 from 开始循环对 i 进行加 step 处理,直到 i 等于 to。step 为正数时,from 比 to 大时不会执行循环处理。step 为负数时,from 比 to 小时不会执行循环处理。

    ary = []
    2.step(10, 3) do |i|
    ary << i
    end
    p ary    #=> [2, 5, 8]
     
    ary = []
    10.step(2, -3) do |i|
    ary << i
    end
    p ary    #=> [10, 7, 4]

    如果不对 times、upto、downto、step 的各方法指定块,则会返回 Enumerator 对象。这样,之前通过 step 方法的块获取的一连串数值,就同样也可以通过 Enumerator.collect 方法获取。

    ary = 2.step(10).collect{|i| i * 2}
    p ary    #=> [4, 6, 8, 10, 12, 14, 16, 18, 20]

近似值误差

处理浮点小数时很容易因误差产生问题。这里我们来看看具体的例子,执行下面的程序后会产生意想不到的结果。

a = 0.1 + 0.2
b = 0.3
p [a, b]    #=> [0.3, 0.3]
p a == b    #=> false

虽然我们期待 0.1 + 0.2 与 0.3 的比较结果为 true,但实际结果却是 false。为什么会这样呢?

在 10 进制中,就像 1/10、1/100、1/1000……这样,我们会用 10 取幂后的倒数来表示数值。而另一方面,Float 类的浮点小数则是用 2 取幂后的倒数来表示,如 1/2、1/4、1/8……。因此,在处理 1/5、1/3 这种用 2 进制无法正确表示的数值时,结果就会产生误差。而如果要用 2 进制的和来表示这类数值的话,计算机就必须在适当的位置截断计算结果,这样就产生了近似值误差。

如果可以把小数转换为两个整数相除的形式,那么通过使用 Rational 类进行运算,就可以避免近似值误差。

a = Rational(1, 10) + Rational(2, 10)
b = Rational(3, 10)
p [a, b]    #=> [(3/10), (3/10)]
p a == b

另外,Ruby 还提供了 bigdecimal 库,可以有效处理拥有更多小数位的 10 进制数。

Comparable 模块

Ruby 的比较运算符(==、<= 等)实际上都是方法。Comparable 模块里封装了比较运算符,将其 Mix-in 到类后,就可以实现实例间互相比较的方法。Comparable 在英语中就是“可以比较”的意思。

Comparable 模块封装的方法

  • <>

  • ==

  • >=

  • >

  • between?

Comparable 模块中的各运算符都会使用 <=> 运算符的结果。<=> 运算符如果能像下表那样定义的话,上表中的各个方法就都可以使用。

状态 | 结果

  • | - a <> 时 | -1(比 0 小) a == b 时 | 0 a > b 时 | 1(比 0 大)

下面的 Vector 类表示拥有 x 和 y 两个坐标的向量。为了比较向量间的坐标,这里定义了 <=> 运算符。然后,通过包含(include)Comparable 模块,就可以实现上表中的比较方法。

class Vector
  include Comparable
  attr_accessor :x, :y
 
  def initialize(x, y)
    @x, @y = x, y
  end
 
  def scalar
    Math.sqrt(x ** 2 + y ** 2)
  end
 
  def <=> (other)
    scalar <=> other.scalar
  end
end
 
v1 = Vector.new(2, 6)
v2 = Vector.new(4, -4)
p v1 <=> v2    #=> 1
p v1 < v2      #=> false
p v1 > v2      #=> true
Previous方法Next数组