software foundations LF Poly

更多内容见 https://github.com/january147/software_foundations_notes

原始章节参考 software foundations LF Poly

Polymorphism(多态)

某一些类型,比如list, 其内部存储的元素的类型通常并不影响list本身支持的一些操作(例如添加元素、删除元素、获取长度、获取某个元素等),因此list的定义不应该和其内存存储的元素完全绑定(即不应该针对每种类型的元素都分别定义一个存储其元素的list类型以及对应的行为)。list应该作为一种比普通类型更高阶的类型,从而使得list可以用于存储多种不同的类型,并且针对list的操作应该可以应用到存储不同的类型数据的list中,这样list的操作就体现出了多态性Polymorphism), 即可以应用与多种不同的具体类型。

高阶类型list按照如下方式定义,相比普通的类型,其多了一个类型参数,通过这个参数,可以存储不同类型具体数据的list类型。

Inductive list (X:Type) : Type :=
  | nil
  | cons (x : X) (l : list X).

类似与将接受函数作为参数的函数称为高阶函数,通过其他具体类型构造的类型我将其称为高阶类型

What sort of thing is list itself? A good way to think about it is that the definition of list is a function from Types to Inductive definitions

The parameter X in the definition of list automatically becomes a parameter to the constructors nil and cons – that is, nil and cons are now polymorphic constructors; when we use them, we must now provide a first argument that is the type of the list they are building

隐式类型

定义时使用隐式类型

在某些情况下,我们在定义函数或者constructor时不为其参数变量指定类型,而由coq根据使用具体的定义来自动推导变量类型。

在如下定义中,coq根据后续的使用自动推导了X, x, count三个变量的类型

Fixpoint repeat' X x count : list X :=
  match count with
  | 0 ⇒ nil X
  | S count' ⇒ cons X x (repeat' X x count')
  end.

调用时使用隐式类型

我们也可以在使用函数或者constructor时用填充符号_来代替具体类型作为constructor的参数,由coq根据使用方式自动推导具体类型。例如如下例子使用_代替了nat.

Definition list123' :=
  cons _ 1 (cons _ 2 (cons _ 3 (nil _))).

对于需要类型作为参数的函数或者constructor我们还可以使用Argument命令来将这些参数设置为隐式参数(将隐式参数放到{}中),让coq始终自动推导类型,从而在使用这些函数和constructor时省略掉类型参数。

Arguments nil {X}.
Arguments cons {X} _ _.
Arguments repeat {X} x count.

使用Argument其实是重新声明了函数或者constructor的参数,我们也可以在定义函数(Fixpoint, Definition)时直接使用{}来标识隐式参数. 另外,目前似乎只有类型参数可以是隐式参数。

Definition fold_length {X : Type} (l : list X) : nat :=
 fold (fun _ n => S n) l 0.
The [Arguments] directive specifies the name of the function (or
constructor) and then lists its argument names, with curly braces
around any arguments to be treated as implicit.  (If some
arguments of a definition don't have a name, as is often the case
for constructors, they can be marked with a wildcard pattern
[_].) 

使用Argument定义了隐式参数后,在调用这些函数或者constructor时就不能再显示的传入这些参数

处理无法自动推导隐式类型的情况

通过Argument定义了某些函数和constructor的隐式参数后,可能遇到coq无法自动推导隐式参数的情况.

Definition mynil : list nat := nil.

可以通过两种方法进行处理这个问题。

第一种是通过显示指定目标的类型,使得coq能够进行自动推导

Definition mynil : list nat := nil.

第二种是使用@来暂时性地禁用隐式参数,从而显示的给出参数

Definition mynil' := @nil nat.

针对类型(Type)的Notation

Inductive prod (X Y : Type) : Type :=
| pair (x : X) (y : Y).

prod是一个高阶类型,其接受两个类型作为参数从而构造出一个新的类型,可以使用Notation来定义特定的方式用于调用prod。由于如下定义使用了同自然数乘法mult相同的Notation定义,为了区分两个定义,这里使用: type_scope来表示仅当参数为类型时*才表示prod,而普通值作为参数时*表示mult.

Notation "X * Y" := (prod X Y) : type_scope.

高阶函数

接收函数作为参数或者使用函数作为返回值的函数称为高阶函数. 常见的高阶函数包括了filter, map, flat_map, fold等等

filter

函数filter接收一个函数test,以及一个list,其将list中的每个元素作为参数分别执行test,根据test执行结果来保留其中一部分元素(test执行结果为true的元素)

Fixpoint filter {X:Type} (test: X->bool) (l:list X) : (list X) :=
  match l with
  | [] => []
  | h :: t =>
    if test h then h :: (filter test t)
    else filter test t
  end.

map

函数map接收一个函数f以及一个list,将list中的元素逐个使用f进行转换。

输入 f [ a b c d e]
输出 [ f(a) f(b) f(c) f(d) f(e)]
Fixpoint map {X Y: Type} (f:X->Y) (l:list X) : (list Y) :=
  match l with
  | []     => []
  | h :: t => (f h) :: (map f t)
  end.

flat_map

函数flat_map其也接收一个函数f以及一个list,将list中的元素逐个使用f进行转换,不过这里的函数f接收单个元素并输出一个list, flat_map需要将函数f输出的所有list中的元素取出来放到一个list中去。

假设 f(a) => [a1 a2 a3]
输入 f [ a b c d e]
输出 [ a1 a2 a3 b1 b2 b3 c1 c2 c3 d1 d2 d3 e1 e2 e3]
Fixpoint flat_map {X Y: Type} (f: X -> list Y) (l: list X)
                   : (list Y) :=
match l with
  |nil => nil
  |h :: t => (f h) ++ (flat_map f t)
  end.

fold

函数fold接收一个函数f,一个list以及一个初始值i,其将list中的元素作为函数f的第一个参数传入,将上一个调用f的结果作为f的第二个参数传入,对list中的元素进行逐个处理,并将最后一次调用f得到的结果作为返回值返回。

输入 f [a b c] i
输出 f(c, f(b, f(a, i)))
Fixpoint fold {X Y: Type} (f: X->Y->Y) (l: list X) (b: Y)
                         : Y :=
  match l with
  | nil => b
  | h :: t => f h (fold f t b)
  end.

匿名函数

某些短小的,特定的函数可能不需要为其定义一个名称,此时可以使用匿名函数来定义, 匿名函数使用关键字fun来定义

fun l => (length l) =? 1)

如下例子使用匿名函数作为调用高阶函数的参数

Example test_filter2':
    filter (fun l => (length l) =? 1)
           [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]
  = [ [3]; [4]; [8] ].
Proof. reflexivity. Qed.

command

Arguments

使用Argument可以重新声明了函数或者constructor的参数,其主要功能(不确定)是将已定义函数或者constructor的类型参数改为隐式参数,以下例子使用{}将constructor nil, cons以及函数repeat中的类型参数X声明为隐式参数,之后调用时将不能再显式传入这些参数。

Arguments nil {X}.
Arguments cons {X} _ _.
Arguments repeat {X} x count.

Fail

Fail表示后续的语句将会发生错误,coq在运行时确保Fail后的语句确实发生了错误(没有发生错误则会发出警告),并且不会因为这些错误而停止执行。

Fail Definition mynil := nil.

The [Fail] qualifier that appears before [Definition] can be used with any command, and is used to ensure that that command
indeed fails when executed. If the command does fail, Coq prints the corresponding error message, but continues processing the rest of the file.

重要练习

定义fold_map

fold_map即使用fold函数来实现map函数。我们知道fold函数将list A转换为B,我们这里的fold_map需要将list X转换为list Y,因此,要使用fold来实现mapfold中的B必须等于map中的list Y。因此我们传入fold函数的函数如下

fun x (y : list Y) => (f x) :: y)

其第一个参数类型为X,第二个参数类型是list Y,执行的操作为将第一个参数x传递给函数f, 并将函数f输出结果加入第二个参数y(注意y是一个list)中. 并返回增加了元素f x之后的y.

使用fold实现的map以及正确性证明如下:

Definition fold_map {X Y: Type} (f: X -> Y) (l: list X) : list Y := fold (fun x (y : list Y) => (f x) :: y) l nil. 
  
Theorem fold_map_correct: forall (X Y : Type) (f: X->Y) (l: list X), fold_map f l = map f l.
Proof.
  intros X Y f l. induction l as [|h t Hl].
  -simpl. reflexivity.
  -simpl. rewrite <- Hl. reflexivity.
Qed. 

基于高阶函数定义的自然数的乘方运算

将函数n f表示一个将f嵌套执行n次的函数,函数m n则表示一个将函数n嵌套执行m次的函数。我们知道n f表示将f嵌套执行n次,那么 n (n f)则表示将f嵌套执行n * n次,n (n (n f))则表示将f嵌套执行n * n * n次。因此,将函数n嵌套执行m次得到的函数作用于f则会得到一个将f执行n * n * n * n...共计mn相乘的结果次的函数,这就是我们所需的exp(乘方运算)的定义.

Definition exp (n m : cnat) : cnat := fun (X : Type) (f : X -> X) (x : X) => (m (X -> X) (n X) f) x.
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值