Beginning Scala study note(4) Functional Programming in Scala
1. Functional programming treats computation as the evaluation of mathematical and avoids state and mutable data. Scala encourages an expression-oriented programming(EOP)
1) In expression-oriented programming every statement is an expression. A statement executes code, but does not return any value. An expression returns value. Note that an expression-oriented programming language is a programming language where every construct is an expression, and thus evaluates to a value.
- scala> val test = if(>) "true" else "false"
- test: String = true
The relating java code:
- boolean test = > ? true : false;
Scala has unified the concept of ?: with its blocks and so Scala has no ?: syntax.
2) An expression is referentially transparent(引用透明) if it can be substituted(替代) by its resulting value, without changing the behavior of the program, regardless of where the expression is used in the program. The keystones of functional programming are: referential transparency, higher-order function, and immutable value. A pure function does not mutate the input parameters and always returns the same value for the same input.
The syntax for a function literal(函数字面量) with a parenthesized comma-separated list of arguments followed by an arrow and the body of the function. It's also called an anonymous function.
A function value is a function object and you can invoke the function object in the same manner as you invoke any other function. The function object extends one of the FunctionN traits, FunctionN depends on the number of arguments.
- scala> val add = (x: Int, y: Int) => x + y
- add: (Int, Int) => Int = <function2>
- scala> add(,)
- res0: Int =
The invocation of this function is converted to a call to the apply method of the Function class instance.
- scala> val areaOfRectangle:(Int, Int) => Int = (width: Int, height: Int) => {width*height}
- areaOfRectangle: (Int, Int) => Int = <function2>
- scala> areaOfRectangle(,)
- res1: Int =
Trait scala.Function2 in the Scala package:
trait Function2[-T1, -T2, +R] extends AnyRef { ... abstract def apply( v1 :T1, v2 :T2 ) : R ... }
- scala> val areaOfRectangle: Function2[Int,Int,Int] = (width: Int, height: Int) => width*height
- areaOfRectangle: (Int, Int) => Int = <function2>
You can explicitly call the apply method:
- scala> areaOfRectangle.apply(,)
- res4: Int =
You can even define a function by implementing an appropriate Function Trait and define its required apply method.
- scala> val areaOfRectangle: (Int, Int) => Int = new Function2[Int, Int, Int]{
- | def apply(width: Int, height: Int):Int = {
- | width*height
- | }
- | }
- areaOfRectangle: (Int, Int) => Int = <function2>
2. A first-class function is a function that can be: 1) Assigned to variables, 2) Passed as an argument to the other function, and 3) Returned as values from the other function. And such functions, which take functions as arguments or return a function, are called higher-order(高阶) functions.
1) Function as Variable
- scala> val doubler = (i: Int) => {i*}
- doubler: Int => Int = <function1>
- scala> doubler()
- res0: Int =
The variable doubler is an instance of a function, known as a function value.
2) Function as Parameter
- scala> def operation(functionparam: (Int, Int) => Int){
- | println(functionparam(,))}
- operation: (functionparam: (Int, Int) => Int)Unit
- scala> val add = (x: Int, y: Int) => {x+y}
- add: (Int, Int) => Int = <function2>
- # Any function that matches this signature can be passed into the operation method
- scala> operation(add)
- scala> val subtract = (x: Int, y: Int) => {x-y}
- subtract: (Int, Int) => Int = <function2>
- scala> operation(subtract)
- scala> val multiply = (x: Int, y: Int) => {x*y}
- multiply: (Int, Int) => Int = <function2>
- scala> operation(multiply)
3) Returning a Function
You can return a function from a function or method. In order to do this, first define an anonymous function.
- # define an anonymous function
- scala> def greeting = (name: String) => {"hello "+name}
- greeting: String => String
- # now you can assign greeting() to a variable
- scala> val greet = greeting
- greet: String => String = <function1>
- scala> greet("World")
- res4: String = hello World
4) Closure
A closure is a function, whose return value depends on the value of one or more variables declared outside this function.
- scala> var y =
- y: Int =
- scala> val multiplier = (x: Int) => x * y
- multiplier: Int => Int = <function1>
- scala> multiplier()
- res0: Int =
- scala> y =
- y: Int =
- scala> multiplier()
- res2: Int =
The multiplier function references y and reads its current value each time. The Scala compiler creates a closure that encompass the variable in the enclosing scope.
5) Partially Applied Function
When all parameters are passed to the function you have fully applied the function to all the parameters.
- scala> val add = (x: Int, y: Int) => x + y
- add: (Int, Int) => Int = <function2>
- scala> add(,)
- res3: Int =
When you give only a subset of the parameters to the function, the result of the expression is a partially applied function. It shows partiallyAdd is a fuction that implements the Function1 trait.
- scala> val partiallyAdd = add(, _:Int)
- partiallyAdd: Int => Int = <function1>
- scala> partiallyAdd()
- res4: Int =
The first argument 1 was passed int the original add function and the new function named partiallyAdd was created, which is a partially applied function; then, the second argument 10 was passed into partiallyAdd. When you provide all the parameters, the original function is executed, yielding the result.
6) Curried Function
Currying converts a function with multiple parameters creating a chain of function, each expecting a single parameter.
- scala> val add = (x: Int, y: Int) => x + y
- add: (Int, Int) => Int = <function2>
- # curried functions
- scala> def add(x: Int)(y: Int) = x + y
- add: (x: Int)(y: Int)Int
- scala> add()()
- res10: Int =
- # another form
- scala> def add(x: Int) = (y: Int) => x + y
- add: (x: Int)Int => Int
- scala> add()()
- res11: Int =
7) Function Composition
An implication of composability is that functions can be treated as values.
- sealed trait Expr # sealed仅能被同文件中的类继承
- case class Add(left: Expr, right: Expr) extends Expr
- case class Mul(left: Expr, right: Expr) extends Expr
- case class Val(value: Int) extends Expr
- case class Var(name: String) extends Expr
We can build expressions like:
- + => Add(Val(), Val())
- * ( + ) => Mul(Val(),Add(Val(), Val())
- a * => Mul(Var("a"), Val())
- scala> def calc(expr: Expr, vars: Map[String, Int]):Int = expr match{
- | case Add(left,right) => calc(left, vars) + calc(right, vars)
- | case Mul(left,right) => calc(left, vars) + calc(right, vars)
- | case Val(v) => v
- | case Var(name) => vars(name)
- | }
If expr is an Add, we extract the left and right parameters, which are themselves Exprs. We call calc to calculate the value of the left and right parameters and add the results.
8) Tail Calls and Tail Call Optimization
A recursive function is one that may invoke itself.
- scala> def factorial(number: Int): Int = {
- | if(number == )
- | return
- | number * factorial(number-)
- | }
- factorial: (number: Int)Int
- scala> println(factorial())
The Scala compiler can optimize recursive functions with tail recursion so that recursive calls do not use all the stack space, therefore not running into stack-overflow error. Only functions whose last statement is the recursive invocation can be optimized for tail-recursion by the Scala compiler.
Scala provides an annotation available to mark a function to be optimized for tail-recursion. A function marked with the annotation causes an error at compilation time if it cannot be optimized for tail-recursion. Add @annotation.tailrec before the function definition.
- # The recursive call is not the last statement
- scala> @annotation.tailrec
- | def factorial(number: Int): Int = {
- | if(number == )
- | return
- | number * factorial(number - )
- | }
- <console>:: error: could not optimize @tailrec annotated method factorial: it contains a recursive call not in tail position
- number * factorial(number - )
- ^
- scala> @annotation.tailrec
- | def factorial(accumulator: Int, number: Int): Int = {
- | if(number == 1)
- | return accumulator
- | factorial(number * accumulator, number - 1)
- | }
- factorial: (accumulator: Int, number: Int)Int
A successful compile guarantees that the function will be optimized with tail recursion, so that each successive call will not add new stack frames.
9) Call-by-Name, Call-by-Value, and General Laziness
In java programs, when you call a method with parameters, the value of the parameters are all calculated before the method is called. There are some cases when you want parameters to be optionally evaluated or repeatedly evaluated. In this case, Scala provides the call-by-name mechanism.
- # java code of log messages
- if(logger.level().intValue() >= INFO.intValue()){
- logger.log(INFO,"The value is "+value)
- }
- # Call-by-name has the ability to delay the evaluation of the String to log only if that String will actually be logged
- def log(level: Level, msg: => String) =
- if(logger.level().intValue() >= INFO.intValue())
- logger.log(level, msg)
- # Call this code
- log(INFO, "The value is "+value)
The log method will access '"The value is "+value' only if the log message is going to be printed. In order to make something call-by-name, just put => before the type.
The first use of call-by-name is passing an expression that takes a long time to evaluate that may not be evaluated. The second use for call-by-name is the situation where we want to evaluate the expression many times in the target method.
- # we could collect all the Strings returned from an expression until we encounter a null
- def allString(expr: => String): List[String] = expr match {
- case null => Nil
- case s => s :: allStrings(expr)
- }
- scala> import java.io._
- import java.io._
- scala> val br = new BufferedReader(new FileReader("nohup.out"))
- br: java.io.BufferedReader = java.io.BufferedReader@2d33ee43
- scala> allString(br.readLine)
- res5: List[String] = List(,,,)
- scala> for(str <- res5) println(str)
- # scala method
- import scala.io.Source
- if(args.length > ){
- for(line <- Source.fromFile(args()).getLines())
- println(line.length+" "+line)
- }else
- Console.err.println("Please enter filename")
Beginning Scala study note(4) Functional Programming in Scala的更多相关文章
- Beginning Scala study note(3) Object Orientation in Scala
1. The three principles of OOP are encapsulation(封装性), inheritance(继承性) and polymorphism(多态性). examp ...
- Beginning Scala study note(1) Geting Started with Scala
1. Scala is a contraction of "scalable" and "language". It's a fusion of objecte ...
- Beginning Scala study note(6) Scala Collections
Scala's object-oriented collections support mutable and immutable type hierarchies. Also support fun ...
- Beginning Scala study note(9) Scala and Java Interoperability
1. Translating Java Classes to Scala Classes Example 1: # a class declaration in Java public class B ...
- Beginning Scala study note(5) Pattern Matching
The basic functional cornerstones of Scala: immutable data types, passing of functions as parameters ...
- Beginning Scala study note(2) Basics of Scala
1. Variables (1) Three ways to define variables: 1) val refers to define an immutable variable; scal ...
- Beginning Scala study note(8) Scala Type System
1. Unified Type System Scala has a unified type system, enclosed by the type Any at the top of the h ...
- Beginning Scala study note(7) Trait
A trait provides code reusability in Scala by encapsulating method and state and then offing possibi ...
- Coursera公开课Functional Programming Principles in Scala习题解答:Week 2
引言 OK.时间非常快又过去了一周.第一周有五一假期所以感觉时间绰绰有余,这周中间没有假期仅仅能靠晚上加周末的时间来消化,事实上还是有点紧张呢! 后来发现每堂课的视频还有相应的课件(Slide).字幕 ...
随机推荐
- CSS3 速移动效果动画流畅无卡顿
js或jquery 元素移动以像素计算,手机上移动效果会有卡顿 利用CSS3 可以很简单的实现流畅的移动动画 transform: translate3d(66px, 88px, 0px) rotat ...
- 抓取百万知乎用户信息之HttpHelper的迭代之路
什么是Httphelper? httpelpers是一个封装好拿来获取网络上资源的工具类.因为是用http协议,故取名httphelper. httphelper出现的背景 使用WebClient可以 ...
- javascript闭包
关于闭包的介绍,推荐两篇文章: 廖雪峰javascript教程-闭包: http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a ...
- 9Spring进阶----青软S2SH(笔记)
- 耗时两月,NHibernate系列出炉
写在前面 这篇总结本来是昨天要写的,可昨天大学班长来视察工作,多喝了点,回来就倒头就睡了,也就把这篇总结的文章拖到了今天. nhibernate系列从开始着手写,到现在前后耗费大概两个月的时间,通过总 ...
- D2js 是如何处理并发的
d2js 运行于 servlet 容器,如tomcat,由于容器自身支持并发,似乎 d2js 只要使用 nashorn 运行脚本即可.这样我们得到最简单的实现方式: 在该方式中,nashorn引擎仅存 ...
- 转VS2010解决方案转换到VS2008
原文链接地址:http://www.codeproject.com/Tips/80953/Converting-VS2010-Solution-to-VS2008 如果你使用VS2010的任何版本 ...
- 关于GridView中控件的问题
最近做项目报表时,会遇到在Gridview中有一些控件,报表中也会有更新.删除等一系列的操作,但往往会遇到一些控件取值取不到或者找不到控件得问题,通过网上查阅资料对其中的一些做一总结: 前台代码如下: ...
- Android Studio导入第三方类库的方法
Android Studio导入第三方类库的方法 本人也刚刚开始尝试做android app的开发,听说android studio是Google支持的android 应用开发工具,所以想应该肯定比E ...
- go:结构体的可访问性
1.要使某个符号对其他包( package)可见(即可以访问),需要将该符号定义为以大写字母开头------摘自go相关书籍2.go只限制包内外的可访问性,而不限制同包内不同文件的可访问性 本文讨论结 ...