Scala学习笔记——函数式编程

在函数式编程中,函数式第一等级的值,就像数据变量的值一样,你可以从函数中组合形成新函数(如`tan(x)=sin(x)/cos(x))`,可以将函数赋值给变量,也可以将函数作为参数传递给其它函数,还可以将函数作为其它函数的返回值。

当一个函数采用其它函数作为变量或返回值时,它被称为高阶函数。

object FactorialApp extends App {

  def factorial(i: Int): Long = {
    def fact(i: Int, accumulator: Int): Long = {
      if (i <= 1) accumulator
      else fact(i - 1, i * accumulator)
    }
    fact(i, 1)
  }

  (0 to 5) foreach (i => println(factorial(i)))

  var sum = (1 to 5) filter (_ % 2 == 0) map (_ * 2) reduce (_ + _)
  var fact = (1 to 5) filter (_ > 4) map (_ * 2) reduce (_ * _)
  print(sum)
  print(fact)
}
闭包
 //闭包
  var factor = 2
  val multiplier = (i: Int) => i * factor
  var b = (1 to 10) filter (_ > 4) map multiplier reduce (_ * _)
  println(b)
  FactorialApp.factor = 3
  var c = (1 to 10) filter (_ > 4) map FactorialApp.multiplier reduce (_ * _)
  println(c)
偏函数

 偏作用函数是一个表达式,带部分而非全部参数列表的函数。返回值是一个新函数,新函数负责携带剩下的参数列表。 偏函数则是带参数的函数,并未对该类型的所有值都有定义。偏函数的字面量语法由包围在花括号中的一个或多个case语句构成:

object PartialFunctionApp extends App {

  def cat1(s1: String)(s2: String) = s1 + s2

  //偏应用函数
  val hello = cat1("hello ") _

  println(hello("world"))
  println(cat1("hello ")("world"))


  //偏应用函数
  def add(x: Int, y: Int) = x + y

  val addOne = add(1, _: Int)

  println(addOne(4)) // 5
  println(addOne(6)) // 7


  val inverse:PartialFunction[Double, Double] = {
    case d if d != 0.0 => 1.0 /d
  }

  println(inverse(2.0))

}
Curry化的函数

Curry将一个带有多个参数的函数转换为一系列函数,每个函数都只有一个参数

package base.functionT


object CurryApp extends App {

  def add(x: Int, y: Int) = x + y

  //add函数柯里化之后:
  //  def add(x: Int) = (y: Int) => x + y
  //简化为
  //  def add(x: Int)(y: Int) = x + y

  val add1 = (add _).curried

  println(add1(2)(3)) // 5
  println(add1(2) {
    5
  }) // 7


  def multiplier(i: Int)(factor: Int) = i * factor

  val byFive = multiplier(5) _
  val byFour = multiplier(4) _
  println(byFive(5))
  println(byFour(5))


  def map[A, B](xs: List[A])(func: A => B) = xs.map {
    func(_)
  }

  // List[String] = List(11, 21, 31)
  map(List(1, 2, 3)) {
    x => x + "1"
  }

}
方法与函数

方法:方法指的是定义在类中的方法
函数:函数在scala中代表1个类型和一个对象,方法却不会,方法只会出现在类中。

def max(x: Int, y: Int): Int = if (x > y) x else y
def max = (x: Int, y: Int) => if (x > y) x else y 

两者都定义为「方法(Method)」,但后者返回了一个函数(Function)类型。因此,后者常常也被习惯地称为函数(Function)。首先,它们两者可以具有相同的调用形式:max(1, 2)。但对于后者,调用过程实际上包括了两个子过程。

首先调用max返回(Int, Int) => Int的实例;
然后再在该函数的实例上调用apply方法,它等价于:

max.apply(1, 2)

其次,两者获取函数值的方式不同。后者可以直接获取到函数值,而对于前者需要执行η扩展才能取得等价的部分应用函数。

val f = max _

此时,f也转变为(Int, Int) => Int的函数类型了。实施上,对于上例,η扩展的过程类似于如下试下。

val f = new (Int, Int) => Int {
  def apply(x: Int, y: Int): Int = max(x, y)
}
val与def

def用于定义方法,val定义值。对于「返回函数值的方法」与「直接使用val定义的函数值」之间存在微妙的差异,即使它们都定义了相同的逻辑。例如:

val max = (x: Int, y: Int) => if (x > y) x else y 
def max = (x: Int, y: Int) => if (x > y) x else y
语义差异

 虽然两者之间仅存在一字之差,但却存在本质的差异。
 def用于定义「方法」,而val用于定义「值」。
def定义的方法时,方法体并未被立即求值;而val在定义时,其引用的对象就被立即求值了。def定义的方法,每次调用方法体就被求值一次;而val仅在定义变量时仅求值一次。
例如,每次使用val定义的max,都是使用同一个函数值;也就是说,如下语句为真。

max eq max   // true

而每次使用def定义的max,都将返回不同的函数值;也就是说,如下语句为假。

max eq max   // false

其中,eq通过比较对象id实现比较对象间的同一性的。

类型参数

val代表了一种饿汉求值的思维,而def代表了一种惰性求值的思维。但是,def具有更好可扩展性,因为它可以支持类型参数。

def max[T : Ordering](x: T, y: T): T = Ordering[T].max(x, y)

lazy惰性

def在定义方法时并不会产生实例,但在每次方法调用时生成不同的实例;而val在定义变量时便生成实例,以后每次使用val定义的变量时,都将得到同一个实例。
lazy的语义介于def与val之间。首先,lazy val与val语义类似,用于定义「值(value)」,包括函数值。

lazy val max = (x: Int, y: Int) => if (x > y) x else y 

其次,它又具有def的语义,它不会在定义max时就完成求值。但是,它与def不同,它会在第一次使用max时完成值的定义,对于以后再次使用max将返回相同的函数值。

参数传递

Scala存在两种参数传递的方式。

  • Pass-by-Value:按值传递
  • Pass-by-Name:按名传递
    按值传递
    默认情况下,Scala的参数是按照值传递的。
def and(x: Boolean, y: Boolean) = x && y

对于如下调用语句:

and(false, s.contains("horance"))

表达式s.contains("horance")首先会被立即求值,然后才会传递给参数y;而在and函数体内再次使用y时,将不会再对s.contains("horance")表达式求值,直接获取最先开始被求值的结果。

传递函数

将上例and实现修改一下,让其具有函数类型的参数。

def and(x: () => Boolean, y: () => Boolean) = x() && y()

其中,() => Boolean等价于Function0[Boolean],表示参数列表为空,返回值为Boolean的函数类型。

调用方法时,传递参数必须显式地加上() =>的函数头。

and(() => false, () => s.contains("horance"))

此时,它等价于如下实现:

and(new Function0[Boolean] { 
  def apply(): Boolean = false
}, new Function0[Boolean] {
  def apply(): Boolean = s.contains("horance")
}

此时,and方法将按照「按值传递」将Function0的两个对象引用分别传递给了x与y的引用变量。但时,此时它们函数体,例如s.contains("horance"),在参数传递之前并没有被求值;直至在and的方法体内,x与y调用了apply方法时才被求值。
也就是说,and方法可以等价实现为:

def and(x: () => Boolean, y: () => Boolean) = x.apply() && y.apply()
按名传递

通过Function0[R]的参数类型,在传递参数前实现了延迟初始化的技术。但实现中,参数传递时必须构造() => R的函数值,并在调用点上显式地加上()完成apply方法的调用,存在很多的语法噪声。

因此,Scala提供了另外一种参数传递的机制:按名传递。按名传递略去了所有()语法噪声。例如,函数实现中,x与y不用显式地加上()便可以完成调用。

def and(x: => Boolean, y: => Boolean) = x && y

其次,调用点用户无需构造() => R的函数值,但它却拥有延迟初始化的功效。

and(false, s.contains("horance"))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值