博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
函数式编程笔记 04
阅读量:5938 次
发布时间:2019-06-19

本文共 2478 字,大约阅读时间需要 8 分钟。

练习:找不动点

数x叫做一个函数的不动点,如果f(x) = x。

程序:

object exercise {  val tolerance = 0.0001                          //> tolerance  : Double = 1.0E-4  def abs(x: Double) = if (x >= 0) x else -x      //> abs: (x: Double)Double  def isCloseEnough(x: Double, y: Double) =    abs((x - y) / x) / x //> isCloseEnough: (x: Double, y: Double)Boolean  def fixedPoint(f: Double => Double)(firstGuess: Double) = {    def iterate(guess: Double): Double = {      val next = f(guess)      if(isCloseEnough(guess, next)) next      else iterate(next)    }    iterate(firstGuess)  }                                               //> fixedPoint: (f: Double => Double)(firstGuess: Double)Double}复制代码

之前的sqrt函数可以改为求y = x / yy值,即y => x / y的不动点。

def sqrt(x: Double) =    fixedPoint(y => x / y)(1.0)复制代码

如果按上面这样写就会陷入循环,比如sqrt(2)时每次的guess在1.0、2.0反复。一个解决的办法是平均原序列的后继值:

def sqrt(x: Double) =  fixedPoint(y => (y + x / y) / 2)(1.0)复制代码

其实这种求平均以稳定化的方法很普遍,可以被抽取成一个函数。

def averageDamp(f: Double => Double)(x: Double) = (x + f(x)) / 2复制代码

然后sqrt函数就可以这样写了:

def sqrt(x: Double) = fixedPoint(averageDamp(y => x / y))(1.0)复制代码

语法什么的不想写了……

类的求值方法和函数的一样,都是call-by-value。就是说new C(e1, …, em)会变成new C(v1, …, vm)

现在假设有这样一个类定义:

class C(x1, …, xm) { … def f(y1, …, yn) = b …}复制代码

那么下面这个表达式怎么被求值?

new C(v1, …, vm).f(w1, …, wn)复制代码

答案是,它会被重写成:

[w1/y1, …, wn/yn] [v1/x1, …, vm/xm] [new C(v1, …, vm)/this] b复制代码

其中有三个替换过程:

  1. 函数f的参数被求值
  2. C的参数被求值
  3. this引用被new C(v1, …, vn)替换

看例子:

new Rational(1, 2).number-> [1/x, 2/y] [] [new Rational(1, 2)/this] x=  1复制代码
new Rational(1, 2).less(new Rational(2, 3))-> [1/x, 2/y] [new Rational(2, 3)/that] [new Rational(1, 2)/this] this.number * that.denom this.denom=  new Rational(1, 2).number * new Rational(2, 3).denom new Rational(2, 3).number * new Rational(1, 2).denom-> 1 * 3 2 * 2-> true复制代码

操作符

之前如果xy是整数,可以写x + y,但如果是Rational,则要写成r.add(s)。在Scala中,可以消除这种差异。

中缀标记

只有一个参数的方法可以像中缀操作符那样用。

r add s == r.add(s)r less s == r.less(s)r max s == r.max(s)复制代码

宽松的标识符

操作符可以用作标识符。

标识符可以是:

  • 字母组成:字母开头,后跟一系列字母或数字
  • 符号组成:符号开头,后跟其他的符号
  • 下划线_算作字母
  • 字母组成的标识符也能以下划线后跟一些符号结束

标识符例子:

x1    *    +?%&    vector_++    counter_=复制代码

然后就可以这样定义方法:

def + (r: Rational) = …def - (r: Rational) = …复制代码

前置标识符也是可以的,方法要以unary_开头:

def unary_- : Rational = new Rational(-number, denom)复制代码

这样用:

x = new Rational(1, 2)-x复制代码

如果方法名以符号结尾则要把之后的:用空格隔开,要不就会被误认为是方法名的一部分了。

优先级

操作符优先级由首字母决定。

优先级的升序排列:

  • 所有字母
  • |
  • ^
  • &
  • = !
  • :
    • / %
  • 所有其他运算符

练习:给出下面的表达式的顺序

a + b ^? c ?^ d less a ==> b | c复制代码

答案是:

((a + b) ^? (c ?^ d)) less ((a ==> b) | c)复制代码

绝不要写这样的代码……

转载地址:http://uyttx.baihongyu.com/

你可能感兴趣的文章
linux日志分割
查看>>
Samba再报安全漏洞
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
Spring学习资料之 依赖注入(一)
查看>>
安装win7提示安装程序无法创建新的系统分区和定位现有系统分区
查看>>
那些年,我跳过的坑(一)
查看>>
快递查询接口的调用与解析案例
查看>>
我的友情链接
查看>>
【MYSQL】SQL基本写法
查看>>
chmod 777 修改权限 - [Linux相关]
查看>>
服务器性能优化配置建议
查看>>
物理网卡在ESXi/ESX服务器中的推荐配置方式
查看>>
实战浪潮英信服务器web部署操作过程(3)
查看>>
Spring 定时任务实现 以及无法正常执行分析
查看>>
Linux基础(11)文本处理三剑客之sed
查看>>
Zabbix3.2.6之通过JMX监控Tomcat
查看>>
重新定义工作站的“边界”
查看>>
第三方推送已死
查看>>
回档|神经网络
查看>>