1、程序设计方法2、应用实例3、实验作业实验内容学习R语言中程序设计方法实验目的程序设计程序控制结构•R是一个表达式语言(expressionlanguage),其任何一个语句都可以看成是一个表达式。表达式之间以分号分隔或用换行分隔。表达式可以续行,只要前一行不是完整表达式(比如末尾是加减乘除等运算符,或有未配对的括号)则下一行为上一行的继续。•若干个表达式可以放在一起组成一个复合表达式,作为一个表达式使用。组合用大括号表示,如:•{+x-15;+x;}[1]15•R语言也提供了其它高级程序语言共有的分支、循环等程序控制结构。分支结构(if/else、switch语言)if/else语句•分支结构包括if结构:if(条件)表达式1或if(条件)表达式1else表达式2•其中的“条件”为一个标量的真或假值,表达式可以是用大括号包围的复合表达式。有else子句时一般写成:if(条件){表达式组……}else{表达式组………}这样的写法可以使else不至于脱离前面的if。•例如,如果变量lambda为缺失值就给它赋一个缺省值,可用:if(is.na(lambda))lambda-0.5;•又比如要计算向量x的重对数,这只有在元素都为正且对数都为正时才能做到,因此需要先检查:if(all(x0)&&all(log(x))0){y-log(log(x));print(cbind(x,y));}else{cat('Unabletocomply\n');}•注意“&&”表示“与”,它是一个短路运算符,即第一个条件为假时就不计算第二个条件。如果不这样此例中计算对数就可以有无效值。•在条件中也可以用“||”(两个连续的竖线符号)表示“或”,它也是短路运算符,当第一个条件为真时就不再计算第二个条件。•在用R编程序时一定要时刻牢记R是一个向量语言,几乎所有操作都是对向量进行的。而R中的if语句却是一个少见的例外,它的判断条件是标量的真值或假值。比如,我们要定义一个分段函数f(x),当x为正时返回1,否则返回0,马上可以想到用if语句实现如下:if(x0)1else0•当x是标量时这个定义是有效的,但是当自变量x是一个向量时,比较的结果也是一个向量,这时条件无法使用。所以,这个分段函数应该这样编程:y=numeric(length(x))y[x0]-1y[x=0]-0#这句可以省略•对于条件语句是向量形式,R提供了ifelse()函数,它的使用格式为ifelse(condition,a,b)•它表示,当condition[i]成立的时候,对应的取值为a[i],否则取值为b[i]。最终返回一个和参数向量同长的向量•x-c(55,60,85,90);a-pass;b-failifelse(x=60,a,b)[1]failpasspasspass•a-c(a1,a2,a3,a4)b-c(b1,b2,b3,b4)ifelse(x=60,a,b)[1]b1a2a3a4若a,b和x不是等长的,则较短的循环使用•有多个if语句时else与最近的一个配对。可以使用if...elseif...elseif...else...的多重判断结构表示多分支。多分支也可以使用switch()函数。•x=0;•if(x0){value-x}elseif(x0){value--x}elseif(x==0){value=ZERO}•value[1]''ZEROswitch语句•switch是多分支语句,其用法为:switch(statement,list);•statement是一个表达式,list是列表,也可以用有名定义。根据表达式与list的关系返回一个值。如果表达式返回值属于1:length(list)中的一个,则返回list中相应位置的值,否则返回NULL。•a-switch(1,2+2,mean(1:10),rnorm(4));a[1]4•a-switch(2,2+2,mean(1:10),rnorm(4));a[1]5.5•x-3a-switch(x,2+2,mean(1:10),rnorm(4));a[1]0.32029210.3057707-0.49221250.1280950•a-switch(4,2+2,mean(1:10),rnorm(4));a[1]NULL•当list是有名定义时,statement等于变量名时,返回该变量名所对应的值,否则返回NULL•x-fruit;a-switch(x,fruit=apple,drink=coffee,vegetable=broccoli);a[1]apple•x-meat;a-switch(x,fruit=apple,drink=coffee,vegetable=broccoli);a[1]NULL循环结构(for/while/repeat)for语句•循环结构中常用的是for循环,是对一个向量或列表的逐次处理,格式为•for(nameinvalues){expression}•如:求1到100的和•sum-0;for(iin1:100){sum-sum+i;i-i+1}sum;[1]5050•当然,如果只是要求各元素的和,只要调用sum(x)即可。所以,编写程序要精简,应尽量避免使用显式循环。•我们再举一个例子。比如,我们要计算同生日的概率。假设一共有365个生日(只考虑月、日),而且各生日的概率是相等的(这里忽略了闰年的情况以及可能存在的出生日期分布的不均匀)。设一个班有n个人,当n大于365时至少两个人生日相同是必然事件。当n小于等于365时,我们可以计算P{至少有两人同生日}=1-P{n个人生日彼此不同},这时,n个人的生日可取值数为365n,而n个人彼此不同的可能数为365中取n个的排列数,彼此不同的概率为P3n65。因此,为了计算n=1,2,...,365的情况下的同生日概率,可以用如下循环实现:•x=numeric(365)for(iin1:365){x[i]=1for(jin0:(i-1))x[i]=x[i]*(365-j)/365x[i]-1-x[i]}这段程序运行了36秒。我们可以尽量用向量运算来实现,速度要快得多:•x-numeric(365)for(nin1:365){x[n]=1-prod((365:(365-n+1))/365)}这段程序只用了1秒。注意不能直接去计算365!,这会超出数值表示范围。•我们有时候需要在某个判定条件成立的时候开始循环,一旦条件不成立,就终止循环,这时可以用while循环语句。•while循环是在开始处判断循环条件的,用法为:while(condition){expression}表示在condition成立的时候,执行expression。•例:一段二分法解方程的程序。•eps-1e-5;while(b-aeps){c-(a+b)/2;if(f(c)0){b-c}else{a-c}}while语句•例:1000以内Fibonacci数列的生成•f-1;f[2]-1;i-1;while(f[i]+f[i+1]1000){f[i+2]-f[i]+f[i+1]i-i+1}f[1]1123581321345589144[13]233377610987•repeat语句是while不一样,把条件加在末尾,依赖break命令跳出循环,基本用法是:•repeat{expressions;if(condition)break}表示一直重复表达式的计算,知道condition成立的时候,跳出循环。repeat语句•例(续):用repeat语句生成1000以内Fibonacci数列•f-1;f[2]-1;i-1;repeat{f[i+2]-f[i]+f[i+1];i-i+1;if(f[i]+f[i+1]=1000)break;}f[1]1123581321345589144[13]233377610987编写自己的函数•R允许用户创建自己的函数。事实上,R本身提供的绝大多数函数如sum(),plot()等,是编写人员写在R中的,与用户自己创建的函数没有本质上的区别。•R中函数定义的一般格式为function.name-function(arg1,arg2,…){expressions}•其中arg是函数的参数(自变量);expressions是一组表达式;放在程序最后一行的是返回值,也可以加return()命令返回指定的内容,返回值可以是向量、数组、列表或数据框。•定义函数可以直接在命令行进行编写,例如•hello=function(){cat(Hello,world\n);•hello#查看函数具体内容function(){cat(Hello,world\n);}•hello()#运行函数Hello,world•函数体为一个复合表达式,各表达式的之间用换行或分号分开。不带括号调用函数显示函数定义,而不是调用函数。•在命令行输入函数程序很不方便修改,所以我们一般是打开一个其他的编辑程序(如Windows的记事本),输入以上函数定义,保存文件,比如保存到了C:\R\hello.R,我们就可以用•source(“Hi.R)•运行文件中的程序。实际上,用source()运行的程序不限于函数定义,任何R程序都可以用这种方式编好再运行,效果与在命令行直接输入是一样的。•对于一个已有定义的函数,可以用fix()函数来修改,如:•fix(hello)•将打开一个编辑窗口显示函数的定义,修改后关闭窗口函数就被修改了。fix()调用的编辑程序缺省为记事本,可以用“options(editor=编辑程序名)”来指定自己喜欢的编辑程序。•函数可以带参数,可以返回值,例如:larger-function(x,y){y.is.bigger-(yx);x[y.is.bigger]-y[y.is.bigger];x;}•这个函数输入两个向量(相同长度)x和y,然后把x中比y对应元素小的元素替换为y中对应元素,返回x的值。R返回值为函数体的最后一个表达式的值,不需要使用return()函数。不过,也可以使用“return()”函数从函数体返回调用者。参数(自变量)•R函数调用方式很灵活,例如,如下函数:fsub=function(x,y)x-y有两个虚参数x和y,我们用它计算100-45,可以调用fsub(100,45),或fsub(x=100,y=45),或fsub(y=45,x=100),或fsub(y=45,100)。即调用时实参与虚参可以按次序结合,也可以直接指定虚参名结合。实参先与指定了名字的虚参结合,没有指定名字的按次序与剩下的虚参结合。•函数在调用时可以不给出所有的实参,这需要在定义时为虚参指定缺省值。例如上面的函数改为:•fsub=function(x,y=0)x-y•则调用时除了可以用以上的方式调用外还可以用fsub(100),fsub(x=100)等方式调用,只给出没有缺省值的实参。•即使没有给虚参指定缺省值也可以在调用时省略某个虚参,然后函数体内可以用missing()函数判断此虚参是否有对应实参,如:trans=function(x,scale){if(!missing(scale))x=scale*x…………}•此函数当给了scale的值时对自变量x乘以此值,否则保持原值。这种用法在其它语言中是极其少见的,R可以实现这一点是因为R的函数调用在用到参数的值时才去计算这个参数的值(称为“懒惰求值”),所以可以在调用时缺少某些参数而不被拒绝。•R函数还可以有一个特殊的“...”虚参,表示所有不能匹配的实参,调用时如果有需要与其它虚参结合的实参必须用“虚参名=”的格式引入。例如:•fmin-function(...){for(xinlist(...))cat(mi