Julia 是一门通用编程语言,可用于大多数编程任务。 不过在实际使用中,主要应用场景往往是工程和科学。 快速、灵活且精密的数值计算是其设计的核心。
整数是没有小数点的“整”数。
在基础概念中,我们看到可以把一个整数值赋给变量,而无需指定类型。
为了提高可读性,可以用下划线作为数字分隔符。 编译器会忽略它们。
julia> x = 3
3
julia> typeof(x)
Int64
julia> large_number = 1_234_567_890
1234567890
毫不意外,浮点数有小数点,小数点后还有小数部分。
julia> f = 3.45
3.45
julia> typeof(f)
Float64
当然,也支持科学计数法。
julia> avogadro = 6.02e23
6.02e23
最大值和最小值可能会让你意外:
julia> typemax(Float64)
Inf
julia> typemin(Float64)
-Inf
无穷大也是一个有效值!
正如基础概念中讨论的那样,算术运算符大多与标准算术相同,也就是孩子们学的那种算术。
注意,乘方使用^,而不是**(两者在其他语言中都很常见)。
2 + 3 # 5 (addition)
2 - 3 # -1 (subtraction)
2 * 3 # 6 (multiplication)
8 / 2 # 4.0 (division)
8 % 3 # 2 (remainder)
2 ^ 3 # 8 (exponentiation)
不过,有几个 Julia 特有的细节值得讨论。
julia> x = 4.2
4.2
julia> 2 * x
8.4
julia> 2x
8.4
julia> 2.4x
10.08
这可能让人意外。
总是可以使用*作为中缀运算符,就像大多数其他计算机语言一样。
然而,Julia 的设计者相信,代码应该尽可能看起来像数学等式。
由于变量名必须以字母开头,在名称前加上数字(整数或浮点数)会被视为隐式乘法。
例如,如果想求球体的表面积,我们可以不写4 * pi * r * r,而是这样做:
julia> surface(r) = 4π * r^2
surface (generic function with 1 method)
julia> surface(3)
113.09733552923255
尽管 π 是内置常量,它也是一个(希腊)字母。
因此解析器仍然需要一个显式的*来把π和r分开。
使用/作为中缀运算符时,即使输入是整数,结果也总是浮点数。
对于整数除法,还有更多选择。
julia> 10 / 3 # floating-point division
3.3333333333333335
julia> div(10, 3) # integer division
3
julia> 10 ÷ 3 # synonym for div()
3
julia> 10 // 3 # rational number (fraction)
10//3
div()函数用于整数除法,结果向零截断:正数向下取整,负数向上取整。
作为同义词,我们可以使用中缀运算符÷,同样是为了让它看起来更像数学。
如果你使用能识别 Julia 的编辑器,输入\div,然后按<Tab>键。
//运算符超出了本概念的范围。
现在,我们只需说//的结果是一个“有理”数,大多数人称之为_分数_。
这通常可以自动发生:
julia> x = 2 + 3.5
5.5
julia> typeof(x)
Float64
我们把一个Int64和一个Float64相加,得到了Float64结果。
事实上,在执行加法之前,整数已经被静默转换为Float64。
浮点数到整数转换必然更复杂。 小数点后的部分你想怎么处理?
round()函数会转换为最接近的整数,像 4.5 这样的中间值会舍入到最接近的_偶数_整数。floor()向下取整,ceil()向上取整,trunc()向零取整。Int32(),会失败并抛出InexactError。不过,默认情况下,这些函数不会返回你可能想要的整数类型。 可以指定所需的输出类型。
julia> round(4.5)
4.0
julia> ceil(Int, 4.3)
5
还可以使用digits关键字舍入到指定的小数位数。
julia> round(π, digits=10)
3.1415926536
这难道不是只会抛出错误吗? 事实上,情况并没有那么简单。
使用÷或//进行整数除法会如你所料导致错误。
使用/进行浮点除法时,采用的可能是工程上的做法,而不是标准的计算机科学做法:
julia> 2 / 0
Inf
julia> 0 / 0
NaN
如前文所述,无穷大在 Julia 中是一个有效的浮点数,用Inf表示。
当分子也为零时,结果在数学上是未定义的。
这时 Julia 会把它当作“非数字”,用NaN表示。
你的朋友 Chandler 打算去世界各地充满异国风情的国家旅行。可惜,Chandler 的数学不太好。他非常担心旅途中被货币兑换点坑,所以想让你帮他做一个货币计算器。以下是他对这个应用的具体要求:
创建exchange_money()函数,它接收 2 个形参:
budget:你计划用于兑换的金额。exchange_rate:一单位外币相当于多少本国货币。这个函数应返回兑换后货币的价值。
**注意:**如果你的货币是美元,想把美元按1.20的汇率兑换成欧元,那么1.20 USD == 1 EUR。
julia> exchange_money(127.5, 1.2)
106.25
创建get_change()函数,它接收 2 个形参:
budget:兑换前的金额。exchanging_value:从预算中取走用于兑换的金额。这个函数应返回预算中剩余的金额。
julia> get_change(127.5, 120)
7.5
创建get_value_of_bills()函数,它接收 2 个形参:
denomination:单张纸币的面值。number_of_bills:纸币的总张数。这个兑换点只处理特定面额单位的现金。你收到的总额必须能被一张“纸币”或一个单位的面值整除,因此可能会留下小数或余数。你的函数应只返回兑换点返还的纸币的总价值(不含小数部分)。可惜的是,兑换点会把余数/零头留作额外的奖励。
julia> get_value_of_bills(5, 128)
640
创建get_number_of_bills()函数,它接收amount和denomination。
这个函数应返回在给定的amount内你能收到的纸币张数。换句话说:起始金额能装下多少张整张纸币?记住,你只能收到整张纸币,不能收到零碎的纸币,所以记得相应地做除法。实际上,你是在向下取整到最接近的整张纸币/面值。
julia> get_number_of_bills(127.5, 5)
25
创建get_leftover_of_bills()函数,它接收amount和denomination。
这个函数应返回在给定纸币面值的情况下,起始amount中无法返还的剩余金额。准确知道兑换点能留下多少钱非常重要。
julia> get_leftover_of_bills(127.5, 20)
7.5
创建exchangeable_value()函数,它接收budget、exchange_rate、spread和denomination。
形参spread是作为兑换费收取的百分比,以整数表示。需要将它除以 100 转换成小数。如果1.00 EUR == 1.20 USD,而spread为10,那么实际的总汇率将是:1.00 EUR == 1.32 USD,因为 1.20 的 10% 是 0.12,这笔额外费用会加到兑换中。
这个函数应在计算出汇率加上spread之后,返回新货币的最大价值。记住,货币的面值是整数,不能再细分。
**注意:**返回值应为整数类型。
julia> exchangeable_value(127.25, 1.20, 10, 20)
80
julia> exchangeable_value(127.25, 1.20, 10, 5)
95