[SICP]函数定义

学习函数式编程,从了解变量开始(Scheme之变量)

 

以丘奇的λ演算为根基的函数式语言,函数是重要的程序单元。例如λx.( x+1),即“对于参数x,(计算)x+1”。

Scheme中,使用lambda表达式定义函数,重点为函数的形式参数列表(formal arguments list)和函数体。格式为:

(lambda ( arguments ) (body))

 

1. lambda创建匿名函数

lambda表达式创建一个匿名函数,lambda表达式的值是指向函数的指针(或一个计算过程)。lambda表达式有3个子块,最前面为特殊块lambda,第二个子块为形式参数列表,也叫lambda list,第三个子块为函数体。如求平方的匿名函数为:

(lambda (x) (* x x)) ;Value 19: #[compound-procedure19]

((lambda (x) (* x x)) 2)  → 4

 

2.函数名

【将一个大程序按功能划分成若干个较小的功能模块,称为功能分解或结构化分解(structured decomposition)。如何进行分解?怎样的代码块形式最有效?怎样使小代码块组合起来解决原先的问题呢?这是关于程序组织的基本问题。于是出现了程序开发历史上简单和重要的抽象——子程序或方法[1]子程序(subroutine)是一个由一系列语句组成代码块,拥有一个名字。子程序/方法是对功能分解和功能复合的初步回答。

结构化分解有许多明显的优点,如:

²       封装被多次调用的代码。单一巨体程序中不可避免地会出现“重复的代码”,将它们提取为子程序,符合编写代码的一次且仅仅一次(Once and Only Once) 法则,避免了程序中代码的多次重复,使得该代码块容易修改和维护。

²       形成子程序库。有一些基本的子程序被广泛地应用到各种各样的程序之中,将它们封装为标准子程序,与编译器一起打包和发布,可以为广大程序员重复使用。如C语言中的标准函数、Windows操作系统中动态链接库(dynamic link library)和JDK类库中的方法。

²       减小难度。对于较复杂的问题,将它分解成较简单的小问题,各个击破,分而治之。在面向对象系统中,这一优点体现为类体中的private方法。

子程序成为思考程序组织问题的新平台。子程序会使用其他子程序,也会被其他子程序使用。对一个程序中不同子程序之间相互依赖(连接、互联)程度的度量,称为耦合(coupling)。因而程序设计的基本要求就是使子程序尽可能地独立,即低耦合。同时,需要考虑一个方法的执行对外界或其环境带来什么影响。如果将整个程序当作一个庞大的代码块,则将子程序作为程序的基本构造块,对整个程序采用自顶向下、分而治之的策略分解成大小适度的子程序。一旦实现了所有的子程序,意味着整个程序得以实现。这就是1970s面向对象技术流行之前的结构化编程方法学。】

上述文字说明,函数拥有一个名字,是很重要的事情

(define square (lambda (x) (* x x))   ) ;定义一个变量,绑定lambda表达式的值。

虽然square是一个变量,程序员通常将square称为函数,lambda表达式中的形参即为函数形参。由于定义函数名是常用的代码,Scheme提供了语法糖以简化其输入:

( define (square x) (* x x) )

注意:(square x)是语法糖中函数名和形参的组合,它正好与λ运算中函数应用相像。

(square 2)  → 4 ;;; 函数应用

 

3. 形式参数和返回值

lambda表达式中的形式参数,保留了C系语言对变量的理解,在函数应用时被实参赋值。或者说,相当于数学函数如f(x)=x+1中的自变量,使得函数对一些数加以操作,而且不依赖特定的数。如果实参是函数名,会带来什么?

①编程语言通常会对其元素的使用方式,做出一些限制。而受到的限制最少的元素为第一阶(first-class)元素。第一阶元素可以:

²       用标识符命名

²       作为函数的参数

²       由函数返回。

所以,函数名与文字如4一样,是第一阶元素。

对于函数square,程序员不知道其实参是什么。然而,从其函数体可知,实参必须能够进行*运算。

( define (double x) (+ x x) )

(square (double 2)) → 16

 

②当把函数名作为参数、返回值来使用的时候,就有了更高阶的函数。能够操作函数的函数即为高阶函数(Higher-Order function)

在数学中,序列求和的sigma(∑)记法/符号,就是一个高阶函数的例子。不管f(x)是什么,高阶函数∑求和。

lambda表达式如λx.( x+1)或函数名,并没有返回值的概念。或者说,lambda表达式的值是指向函数的指针(或一个计算过程)

Java语言:(x) ->{ return x+1; }

Scheme语言:(lambda (x) (+ x 1))

只要函数应用时,才可能会计算出一个数据值,如(square 2)。函数应用时,也可以返回一个指向新函数的指针,即以函数作为返回。

函数transf的作用是对函数f进行平移:

(define (transf f)

       (lambda(a)(+a (f a)) ))

((transf square) 2) → 6

函数transf被称为高阶函数,因为从其函数体可知,其参数f能够作为操作符——(f a);而且,transf以函数作为返回,表现为(transf square)作为操作符,(transf square) 是一个匿名函数——((transf square) 2)。

 


高阶函数的意义

函数square不依赖特定的数而进行计算,高阶函数transf不依赖特定的函数而进行计算。因而高阶函数在编程范式上的意义,将数据和函数置于平等地位,使得函数成为第一阶元素

函数和常量被一视同仁,表达了函数式编程的一个基本希望:函数是(一种)数据。当然,要到达“函数是数据”的目标,还需要函数计算时具有一个特点,相同的输入永远有相同的输出,即纯函数。这一基本希望也称为引用透明性。《编程导论(Java )·3.1.2方法4副作用page 99》


[1]粗略地说,子程序、过程、函数或方法都是可用的术语。在不同语言中子程序是通用的术语,这里采用Java中的常用术语“方法”。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值