学习Julia(二)
2013 年 11 月 21 日 | 分类于 学习中接上一篇。本次包括以下一些新的内容:
- 条件判断
- 异常处理
- 表达式(黑魔法)
这一次我们的线索依然是t检验的程序,我们准备进一步完善它。作为比较,我们依然先把R的输出结果贴出来:
t.test(rnorm(10), mu = 0)
data: rnorm(10)
t = 0.768, df = 9, p-value = 0.4622
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
-0.6509637 1.3201667
sample estimates:
mean of x
0.3346015
上一篇中t检验的程序有几个缺陷:
- 没有对数据进行测试,因为t检验样本量至少为2
- 没有显示置信区间(这本质上与下面的几行代码是一样的,这里是偷懒没把它加上)
- 输出结果中小数点位数太多
- 没有显示第一行的
data: rnorm(10)
下面我们一一解决,照例先把代码贴上:
在Julia中产生错误信息的函数与R一样,叫做 error()
。程序第7到第9行是判断 x
的长度,如果不足2,则产生错误信息,退出函数。可以看出,if
语句采用的同样是缩进风格,并需要用 end
来关闭,但省略了R中的圆括号和大括号。
第15行到第17行是计算置信区间的上下界,其中 quantile()
进一步展示了分布函数的操作,这是取分布的分位数,相当于R中的 q
系函数,如 qt()
等。
在输出部分中,与R一样,四舍五入的函数是 round()
,在后面我们对要输出的数值都进行了舍入处理。
最后,本篇中最大的坑来了,R中那行诡异的 data: rnorm(10)
。
可能你会觉得,这行输出与其他的语句没什么区别,按理就是一些字符串操作,但实际情况要复杂得多。让我们先做一个实验。在R里面,如果你运行
t.test(rnorm(100))
那么输出结果中的第一行就是 data: rnorm(100)
。如果你输入的是
t.test(1:10)
输出结果就是 data: 1:10
。如果函数里面是一个已经定义好的变量 x
,那输出结果就是 data: x
。换言之,R会把你输入的表达式原封不动地打印出来。也就是说,当表达式传递给函数时,表达式本身并不会先求值,而是把表达式整体传递给函数。当函数要用到表达式的值时,才会运行这段表达式。这种特性在R中称为“惰性求值”(lazy evaluation)。很晕对不对,确实,让我们看一个简单的例子来帮助理解。
请分别运行下面两段程序:
f1 = function(x)
{
return(NULL);
}
f1(print(1));
f2 = function(x)
{
y = x;
return(NULL);
}
f2(print(1));
输出结果应该是这样的:
> f1(print(1));
NULL
> f2(print(1));
[1] 1
NULL
看到区别了吗?运行 f1(print(1))
的时候这个1并没有被打印出来,而在 f2(print(1))
中却会。这是因为,在 f1()
中,我们根本就没有用到函数的参数 x
,所以表达式传递给函数后没有再被使用,R也就不会计算它的取值。而在 f2()
中,我们需要把 x
的值赋给另一个变量,这样就必须对其进行计算。在R中,我们可以通过 substitute()
函数来捕获参数本身的表达式,然后将其转换成字符串,输出到屏幕上。
然而在Julia中,非常遗憾,所有的参数在传递之前都要计算取值,这是在Julia设计之初就定下的规矩,所以我们无法知道数据是通过何种表达式生成的,也就无法输出类似于 data: rnorm(10)
这样的结果。
当然,我说的是无法实现自动的惰性求值,如果只是要实现表达式的操作,Julia也是可以的。Julia用冒号和括号来表示表达式,比如
:x
:(randn(5) + 2)
:(x + 1)
当表达式只包含一个元素时,它的类型就变成了符号(Symbol),比如上面的 :x
。你甚至可以先把表达式存下来,然后对其取值,或是转换成字符串。
expr = :(x + 1)
x = 1
eval(expr)
string(expr)
总结一下,惰性求值是R中非常独特的一个功能(“作者你够了,这篇文章应该是写Julia的吧!”),最常见的两个例子,一个是 library(MASS)
(或其他任何一个 library()
语句),一个是 plot(rnorm(10), rnorm(10))
。不知道你有没有发现惰性求值存在的痕迹呢?
当然,惰性求值基本属于黑魔法范畴。Julia虽然不支持这一特性,但对于大部分的应用来说,并不会造成太大的问题——它基本属于锦上添花一类,而不是必需的。
最后依然是涉及到的一些R与Julia的对比。
R | Julia | 作用 |
---|---|---|
error() |
error() |
生成错误信息 |
qt(0.975, df) |
quantile(TDist(df), 0.975) |
t分布分位数 |
round(x, 3) |
round(x, 3) |
四舍五入 |
substitute() |
捕获表达式 | |
expression(x + 1) |
:(x + 1) |
生成表达式 |
eval(expr) |
eval(expr) |
表达式求值 |
as.character(x) |
string(x) |
强制转换为字符串 |