轨道
/
Crystal
Crystal
/
练习
/
照片融合师
照片融合师

照片融合师

学习练习

简介

二进制、八进制和十六进制(又称 hex)是基数不同的记数系统。 二进制的基数是 2,八进制是 8,十六进制是 16。 在 Crystal 中,普通的整数是十进制的。 二进制、八进制和十六进制都是整数的表示形式。 这意味着它们表示的是正数和负数(包括零),不带分数或小数,并且支持所有我们能用整数完成的运算。

二进制

二进制是一种基数为 2 的记数系统,只使用数字 0 和 1。 它通常用来表示计算机中电流经过晶体管和开关时的 0(“关闭”)和 1(“开启”)两种状态,以及磁性存储介质中的正负电荷。 二进制可以表示十进制中使用的所有整数。

二进制系统的一小段看起来像这样,不过它会无限延伸,并不会到 128 就结束:

128 64 32 16 8 4 2 1
2 ** 7 2 ** 6 2 ** 5 2 ** 4 2 ** 3 2 ** 2 2 ** 1 2 ** 0

那么,如果我们要表示数字 6,用二进制就是:110

位值 4 2 1
二进制数 1 1 0

运算过程是:4 + 2 + 0 = 6

再比如:19

位值 16 8 4 2 1
二进制数 1 0 0 1 1

这个二进制数是:10011 运算过程是:16 + 0 + 0 + 2 + 1 = 19

Crystal 中的二进制

在 Crystal 中,我们可以用 0b 前缀来表示二进制字面量。 如果写0b10011,Crystal 会把它解释为二进制数,并转换成十进制。

0b10011
# => 19

typeof(0b10011)
# => Int32

如果给一个数字加上 0b 前缀,但后面的内容不是二进制,就会引发编译错误。

0b10211
# => Error: unexpected token: "211"

二进制数的运算

由于二进制数就是整数,我们可以对它们进行一切对整数能做的运算。

0b10011 + 0b10011
# => 38

0b10011 * 0b10011
# => 361

0b10011 / 3
# => 6.333333333333333

二进制表示法的相互转换

Crystal 会自动把二进制字面量转换成 Int32。 要把 Int32 转换成二进制表示,可以使用 <int>.to_s(base) 方法,并把 base 实参设为 2。 该方法会返回这个二进制数的字符串表示:

19.to_s(2)
# => "10011"

要把字符串形式的二进制数转换成整数,可以使用 <int>.to_i(base) 方法,把基数(2)作为实参传进去:

"10011".to_i(2)
# => 19

如果给错基数(或者给出不合法的二进制表示),就会引发 ArgumentError:

"011021".to_i(2)
# => Invalid Int32: "011021" (ArgumentError)

二进制方法

有一些方法可以用在整数上,获取该数字二进制表示的相关信息。

<int>.bit_length 会返回表示该数字所需的位数:

19.bit_length
# => 5

<int>.bit(index) 会返回指定下标处的位,从右边开始数(最低有效位):

19.bit(0)
# => 1

0b10011.bit(1)
# => 1

八进制

八进制是一种基数为 8 的记数系统。 它使用数字 0、1、2、3、4、5、6 和 7。

在 Crystal 中,我们可以用 0o 前缀表示八进制数。 和二进制一样,Crystal 会自动把八进制表示转换成 Int32。

0o123
# => 83

给数字加上 0o 前缀,但后面的内容不是八进制,就会引发编译错误。

八进制表示法的相互转换

和二进制一样,我们可以用 <int>.to_s(base) 方法把 Int32 转换成八进制表示。 也可以用 <int>.to_i(base) 方法把八进制表示转换成 Int32。

83.to_s(8)
# => "123"

"123".to_i(8)
# => 83

和二进制一样,给错基数会引发 ArgumentError。

十六进制

十六进制是一种基数为 16 的记数系统。 它使用数字 0 - 9 和字母 A、B、C、D、E 和 F。 A 是 10,B 是 11,C 是 12,D 是 13,E 是 14,F 是 15。

在 Crystal 中,我们可以用 0x 前缀表示十六进制数。 和二进制、八进制一样,Crystal 会自动把十六进制字面量转换成 Int32。

#=> 0x123
291

给一个非十六进制的数字加上 0x 前缀,会引发编译错误。

十六进制表示法的相互转换

和二进制一样,我们可以用 <int>.to_s(base) 方法把 Int32 转换成十六进制表示。 也可以用 <int>.to_i(base) 方法把十六进制表示转换成 Int32。

291.to_s(16)
# => "123"

"123".to_i(16)
# => 291

和二进制、八进制一样,给错基数会引发 ArgumentError。

精度

把数字转换成字符串表示时,我们可以指定数字的精度。 也就是用来表示该数字的最少位数。 如果数字的位数少于精度,就会在字符串表示的前面补上前导零。 方法是把 precision 实参传给 <int>.to_s(<base>, precision: <value>) 方法。

291.to_s(16, precision: 5)
# => "00123"

543.to_s(precision: 2)
# => "543"

说明

你的朋友厌倦了为照片编辑软件付费,决定自己写一个。 你的朋友决定把这款新软件命名为 Foto Fusionist。

Foto Fusionist 是一款能完成很多操作的照片编辑软件,不过到目前为止你只实现了其中少数几项功能。 不过,你的朋友对目前的进展非常满意,请你再实现几项功能。

1. 把二进制表示转换为整数

第一项功能是,你的朋友希望能够把数字的二进制表示转换为整数。 这样做是因为软件要能读取颜色通道的二进制表示,并将其转换为整数。

实现方法FotoFusionist.binary_to_int,它接收一个类型为String的形参binary。 该方法应返回这个二进制数的整数表示。

FotoFusionist.binary_to_int("10011")
# => 19

2. 从整数中获取最低有效位

你的朋友想要实现的第二项功能是,能够从整数中获取最低有效位。 最低有效位是最靠右的那一位。 它之所以被称为最低有效位,是因为它的值最小。

借助最低有效位,可以判断能否通过减少表示颜色通道所需的位数来优化图像。

实现方法FotoFusionist.least_significant_bit,它接收一个类型为Int32的形参number。 该方法应返回这个数的最低有效位。

FotoFusionist.least_significant_bit(0b10011)
# => 1

FotoFusionist.least_significant_bit(0b10010)
# => 0

3. 从颜色通道获取十六进制值

你的朋友想要实现的第三项功能是,能够从颜色通道中获取十六进制值。 需要十六进制值,是为了能把颜色通道转换为十六进制表示,而且这个十六进制表示必须由两位数字组成。

实现方法FotoFusionist.hex_value,它接收一个类型为Int32的形参number。 该方法应以String的形式返回这个数的十六进制值。

FotoFusionist.hex_value(19)
# => "13"

4. 从 RGB 获取十六进制值

你的朋友想要实现的第四项功能是,能够从 RGB 颜色中获取十六进制值。 这样用户就能得到一种颜色的单个字符串表示。 RGB 颜色是由三个颜色通道表示的颜色:红、绿、蓝。 每个颜色通道都由一个 0 到 255 之间的数字表示。

RGB 颜色的十六进制值是该颜色以十六进制表示的字符串,其中红色通道在前,接着是绿色通道,最后是蓝色通道。

实现方法FotoFusionist.hex_value_from_rgb,它接收三个类型为Int32的形参:red、green和blue。 该方法应以String的形式返回该 RGB 颜色的十六进制值。

FotoFusionist.hex_value_from_rgb(255, 0, 0)
# => "ff0000"
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Crystal Exercism

准备好开始 照片融合师 了吗?

注册 Exercism,借助 26 个概念142 个练习 和真人导师指导,学习并掌握 Crystal,全部免费。