PL真有意思(六):子程序和控制抽象 (2)

C#的代理扩展了对象闭包的概念,代理不仅可以用特殊的对象方法来实例化,也可以用静态函数或者匿名嵌套代理或lambda表达式来实例化。

特殊目的的参数 相似数组

在不同语言中,数组维数和边界的约束时间也不大相同,可推迟到运行时再确定形状的形式数组参数称为相似数组参数或开放数组参数,例如C中的多维数组。

默认参数

默认参数就是调用方可以不提供的参数,如果没有给出就使用预先设置的默认值

实现方式也是直截了当的,调用时如果缺少了某个实际参数,编译器就认为提供的是相应的默认值

命名参数(关键字参数)

在至今为止的讨论中,我们一直假定参数按位置相互对应:第一个实参对应于第一个形参,以此类推。实际上,在一些语言中,如Lisp和Python,这些语言都允许对参数进行命名,命名参数与默认参数结合时特别有用。

命名参数不仅可以使参数以任意顺序描述,还可以起到说明参数用途的作用

可变个数的参数表

在Lisp、Python和C及其后羿的一个不寻常之处,是它允许用户定义一类子程序,这种子程序的参数个数可以变化

在C中,printf可以按如下方式声明:

int printf(char *format, ...)

C中通过内置的函数来获取省略参数

在Java中则是将省略参数包装成一个数组

static void print_lines(String foo, String...lines) 函数返回

对于函数指定返回值的语法,各语言之间区别很大,在Lisp和ML这种不区分表达式和语句的语言中,函数的值就是函数体的值,而函数体本身就是一个表达式

而现在的许多命令式语言都引入了显示的return语句

return expr 泛型子程序和模块

子程序为在许多不同的对象值(参数)上执行某个操作提供了一种很自然的方式。在大型程序中,也常常需要在许多不同的对象类型上做某个操作。

在之前有一篇讲到隐式参数多态性绕过了这个问题,它使我们可以声明一种子程序,其参数类型式没有完全描述的,但仍然是类型安全。但是这种方式,需要将所有的类型检查推迟到类型检查时才来做。

还有一种显式多态性的泛型机制,使一组类似的子程序或模块可以通过唯一一段源代码创建出来。

不同实现方法

泛型特征可以通过多种方式实现。在C++的大多数实现中,它们是一种纯粹的静态机制,创建和使用泛型代码多个实例的所有工作都在编译时完成。在通常情况下,编译器为每个实例创建一个独立代码副本。但是在C++中,为这样每个实例安排独立的类型检查

而在Java中使用一种类型擦除的机制,从效果上看,如果T是Java中的一个泛型类型参数,那么类T的对象将被当作标准基类Object的实例对待,但程序员不需要在将它们用作T类的对象之前插入显式的类型强制,而且编译器可以保证这样的省略的强制不会发生失败。

泛型参数的约束条件

因为泛型也是一种抽象,其接声明的头部应该为抽象的用户提供使用它需要知道的全部信息

在Java和C#中,利用了面向对象和继承的能力来实现。它可以要求某个泛型参数必须支持一组特定的方法

例如在Java中:

public static <T extends Comparable<T>> void sort(T A[]) { } 异常处理

异常可以定义为程序执行过程中出现了没有预料的情况,或者至少是不寻常的情况,而这种情况很难在局部上下文中处理。异常情况可能是由语言实现自动检查的,或者是由程序本身显式引发的。

异常的定义

在许多语言中,动态语义错误会自动产生程序可捕获的异常。程序员还可以定义其它特定于具体应用的异常

在大多数面向对象语言中,异常是某个与风衣或用户定义的类类型的一个实例。

通常使用嵌入在If语句中的throw语句或raise语句来在运行时引发异常。如果一个子程序引发了异常,但是其内部没有捕获,那么它就可能以某种非预期的方式返回。在Java和C++中,在子程序头部包含了一个表,在其中列出可能传播到子程序之外的异常。

异常的传播

在大多数语言中,一个代码块可以由一组异常处理程序,在C++中:

try { } catch(end_if_file) { } catch(io_error_r) { }

在出现异常时,处理程序将出现的顺序检查,控制传入第一个与异常匹配的处理程序。

表达式上的处理程序

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgyzff.html