loop like a functional programing native

63
Loop Like a Functional Programing native Jiaming Zhang 03/22/2017

Upload: jiaming-zhang

Post on 13-Apr-2017

65 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Loop Like a Functional Programing Native

LoopLikeaFunctionalProgramingnativeJiamingZhang

03/22/2017

Page 2: Loop Like a Functional Programing Native

Recap:FPPrinciplesTreatcomputationastheevaluationofmathfunctions

Preferexpressionoverstatement

Preferrecursionoveriteration

PureFunction

Nosideeffect

SameInput->SameOutput

Higher-orderFunction

AvoidMutation

Page 3: Loop Like a Functional Programing Native

Whytalkaboutloop?

Page 4: Loop Like a Functional Programing Native

ScalaTutorial

ListAclassforimmutablelinkedlist

vallist=List(1,2,3)

list.head//=>1list.tail//=>List(2,3)

0::list//=>List(0,1,2,3)

0::1::Nil//=>List(0,1)

List(1,2)++List(3,4)//=>List(1,2,3,4)

ScalaDocumentation-List

Page 5: Loop Like a Functional Programing Native

ScalaTutorial

Stream

ScalaDocumentation-Stream

Page 6: Loop Like a Functional Programing Native

ScalaTutorial

StreamAStreamislikealistexceptthatitselementsarecomputed lazily .

//Streamissimilartolist

valstream=Stream(1,2,3)

stream.head//=>1stream.tail//=>Stream(2,3)

0#::stream//=>Stream(0,1,2,3)

0#::1#::Stream.Empty//=>Stream(0,1)

Stream(1,2)++Stream(3,4)//=>Stream(1,2,3,4)

ScalaDocumentation-Stream

Page 7: Loop Like a Functional Programing Native

ScalaTutorial

Stream//StreamisthelazyversionofList

deffoo:Int={println("Iamcalled")42}

vallist=0::foo::Nil//=>print"Iamcalled"

valstream=0#::foo#::Stream.Empty//=>printnothing

stream.tail//=>print"Iamcalled"//=>returnStream(42)

ScalaDocumentation-Stream

Page 8: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatch

PlayingwithScala’spatternmatching

Page 9: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchItcanmatchconstantjustas switch...case... inJava

defgetNumAsWord(n:Int):String=nmatch{case1=>"One"case2=>"Two"case_=>"NeitherOneNorTwo"}

getNumAsWord(1)//=>"One"getNumAsWord(2)//=>"Two"getNumAsWord(42)//=>"NeitherOneNorTwo"

PlayingwithScala’spatternmatching

Page 10: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchItcanalsounpacksomestructureddata(e.g.tuple,caseclass,list)

PlayingwithScala’spatternmatching

Page 11: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchItcanalsounpacksomestructureddata(e.g.tuple,caseclass,list)

//createtypealiasPersontypePerson=(String,Int)

defAskAge(person:Person)=personmatch{case("Fibonacci",_)=>"Fibonaccidon'twanttotalkabouthisage"case(name,age)=>s"$nameis$ageyearsold"}

PlayingwithScala’spatternmatching

Page 12: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchItcanalsounpacksomestructureddata(e.g.tuple,caseclass,list)

//createtypealiasPersontypePerson=(String,Int)

defAskAge(person:Person)=personmatch{case("Fibonacci",_)=>"Fibonaccidon'twanttotalkabouthisage"case(name,age)=>s"$nameis$ageyearsold"}

AskAge(("Martin",58))//=>"Martinis58yearsold"

AskAge(("Fibonacci",842))//=>"Fibonaccidon'twanttotalkabouthisage"

PlayingwithScala’spatternmatching

Page 13: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchItcanalsounpacksomestructureddata(e.g.tuple,caseclass,list)

//caseclassislikeStructinotherlanguagescaseclassPerson(name:String,age:Int)

defAskAge(person:Person)=personmatch{casePerson("Fibonacci",_)=>"Fibonaccidon'twanttotalkabouthisage"casePerson(name,age)=>s"$nameis$ageyearsold"}

PlayingwithScala’spatternmatching

Page 14: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchItcanalsounpacksomestructureddata(e.g.tuple,caseclass,list)

//caseclassislikeStructinotherlanguagescaseclassPerson(name:String,age:Int)

defAskAge(person:Person)=personmatch{casePerson("Fibonacci",_)=>"Fibonaccidon'twanttotalkabouthisage"casePerson(name,age)=>s"$nameis$ageyearsold"}

AskAge(Person("Martin",58))//=>"Martinis58yearsold"

AskAge(Person("Fibonacci",842))//=>"Fibonaccidon'twanttotalkabouthisage"

PlayingwithScala’spatternmatching

Page 15: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchItcanalsounpacksomestructureddata(e.g.tuple,caseclass,list)

//Listcanbeusedasacaseclassdefhead[T](list:List[T]):T=listmatch{caseList(head,tail)=>headcaseNil=>thrownewjava.util.NoSuchElementException}

PlayingwithScala’spatternmatching

Page 16: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchItcanalsounpacksomestructureddata(e.g.tuple,caseclass,list)

//Listcanbeusedasacaseclassdefhead[T](list:List[T]):T=listmatch{caseList(head,tail)=>headcaseNil=>thrownewjava.util.NoSuchElementException}

//`head::tail`isequivalentto`List(head,tail)`defhead[T](list:List[T]):Option[T]=listmatch{casehead::tail=>headcaseNil=>thrownewjava.util.NoSuchElementException}

PlayingwithScala’spatternmatching

Page 17: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchItcanalsounpacksomestructureddata(e.g.tuple,caseclass,list)

//Listcanbeusedasacaseclassdefhead[T](list:List[T]):T=listmatch{caseList(head,tail)=>headcaseNil=>thrownewjava.util.NoSuchElementException}

//`head::tail`isequivalentto`List(head,tail)`defhead[T](list:List[T]):Option[T]=listmatch{casehead::tail=>headcaseNil=>thrownewjava.util.NoSuchElementException}

head(List(1,2))//=>Some(1)head(List())//=>None

PlayingwithScala’spatternmatching

Page 18: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchHereisanexampleofhowyoucanusepatternmatchingtodealw/differentsubtypes

sealedtraitTreecaseclassBranch(v:Int,left:Tree,right:Tree)extendsTreecaseclassLeaf(v:Int)extendsTree

defsum(t:Tree):Int=tmatch{caseBranch(v,left,right)=>v+sum(left)+sum(right)caseLeaf(v)=>v}

valtree1:Tree=Branch(1,Leaf(1),Leaf(2))valtree2:Tree=Branch(1,Branch(1,Leaf(1),Leaf(2)),Leaf(2))

sum(tree2)//=>7sum(tree1)//=>4

PlayingwithScala’spatternmatching

Page 19: Loop Like a Functional Programing Native

ScalaTutorial

PatternMatchSometimewewanttoperformdiffoperationbasedontheobjecttype.Hereisthecomparisonbetweendoing

thisviapatternmatchandviaJava's instanceof method.

//CompilerwillcomplainbeforeIntegerdoesnothavemethod`toChar`deff(x:Any):String=xmatch{casei:Int=>"Integer:"+i.toChar(0)cases:String=>"String:"+scase_=>"UnknownInput"}

//Compilerwon'tcomplainandexceptionwillraiseatrun-timepublicStringf(Objectx){if(xinstanceofInteger){return"Integer:"+i.charAt(0)}//.....}

PlayingwithScala’spatternmatching

Page 20: Loop Like a Functional Programing Native

UseRecursionQuestion:Definethefactorialfunction fact(n) ,given n>=0

Page 21: Loop Like a Functional Programing Native

UseRecursionQuestion:Definethefactorialfunction fact(n) ,given n>=0

//Imperativedeffact(n:Int):Int={varres=1for(i<-1ton){res*=i}returnres}

Page 22: Loop Like a Functional Programing Native

UseRecursionQuestion:Definethefactorialfunction fact(n) ,given n>=0

//Imperativedeffact(n:Int):Int={varres=1for(i<-1ton){res*=i}returnres}

//Functionaldeffact(n:Int):Int=if(n==0)1elsen*fact(n-1)

Page 23: Loop Like a Functional Programing Native

UseRecursionWhyRecursion?

1. Easytounderstand

2. Easytoproveitscorrectness

Page 24: Loop Like a Functional Programing Native

UseRecursionWhyRecursion?

1. Easytounderstand

2. Easytoproveitscorrectness

deffact(n:Int):Int=if(n==0)1elsen*fact(n-1)

Page 25: Loop Like a Functional Programing Native

UseRecursionWhyRecursion?

1. Easytounderstand

2. Easytoproveitscorrectness

deffact(n:Int):Int=if(n==0)1elsen*fact(n-1)

Simplyafunctioncallusingsubstitution

fact(3)=if(3==0)1else3*fact(3-1)=3*fact(2)=3*(if(2==0)1else2*fact(2-1))=...=3*2*1*1

Page 26: Loop Like a Functional Programing Native

UseRecursionQuestion:Findthelengthofalistusingrecursion

Page 27: Loop Like a Functional Programing Native

UseRecursionQuestion:Findthelengthofalistusingrecursion

deflength[T](list:List[T]):Int=listmatch{caseNil=>0casehead::tail=>1+length(tail)}

Page 28: Loop Like a Functional Programing Native

UseRecursionQuestion:Findthelengthofalistusingrecursion

deflength[T](list:List[T]):Int=listmatch{caseNil=>0casehead::tail=>1+length(tail)}

length(List())//=>0length(List(1,2))//=>2

Page 29: Loop Like a Functional Programing Native

UseRecursionQuestion:Findthelengthofalistusingrecursion

deflength[T](list:List[T]):Int=listmatch{caseNil=>0casehead::tail=>1+length(tail)}

length(List())//=>0length(List(1,2))//=>2

However,thereisaperformanceissuew/thisfunction.

Page 30: Loop Like a Functional Programing Native

UseRecursionQuestion:Findthelengthofalistusingrecursion

deflength[T](list:List[T]):Int=listmatch{caseNil=>0casehead::tail=>1+length(tail)}

length(List())//=>0length(List(1,2))//=>2

However,thereisaperformanceissuew/thisfunction.

valbigList=(1to40000).toList

length(bigList)//=>throwjava.lang.StackOverflowError

Page 31: Loop Like a Functional Programing Native

UseTailRecursionQuestion:Definethefactorialfunction fact(n) ,given n>=0

Page 32: Loop Like a Functional Programing Native

UseTailRecursionQuestion:Definethefactorialfunction fact(n) ,given n>=0

importscala.annotation.tailrec

deffact(n:Int):Int={//@tailrecdoesnotguranteetailrecursion//itsimplyraiseanexceptionwhenthereisnot@tailrecdeffactHelper(n:Int,acc:Int):Int=if(n==0)accelsefactHelper(n-1,acc*n)

factHelper(n,1)}

Page 33: Loop Like a Functional Programing Native

UseTailRecursionQuestion:Findthelengthofalistusingtailrecursion

Page 34: Loop Like a Functional Programing Native

UseTailRecursionQuestion:Findthelengthofalistusingtailrecursion

deflength[T](list:List[T]):Int={deflengthHelper(list:List[T],acc:Int):Int=listmatch{caseNil=>acccasehead::tail=>lengthHelper(tail,acc+1)}lengthHelper(list,0)}

Page 35: Loop Like a Functional Programing Native

UseTailRecursionQuestion:Findthelengthofalistusingtailrecursion

deflength[T](list:List[T]):Int={deflengthHelper(list:List[T],acc:Int):Int=listmatch{caseNil=>acccasehead::tail=>lengthHelper(tail,acc+1)}lengthHelper(list,0)}

length((1to40000).toList)//=>40000

Page 36: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Definethefactorialfunction fact(n) ,given n>=0

Page 37: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Definethefactorialfunction fact(n) ,given n>=0

deffact(n:Int):Int=(1ton).fold(1){(acc,x)=>acc*x}

//Orashorterversiondeffact(n:Int):Int=(1ton).fold(1){_*_}

Page 38: Loop Like a Functional Programing Native

UseHigh-orderFunctionCommonhigh-orderfunctionsforcollections(e.g.array,list)include:

(1to3).map(x=>x*2)//=>Vector(2,4,6)

(0to1).flatMap(x=>(0to2).map(y=>(x,y)))//=>Vector((0,0),(0,1),(1,0),(1,1),(2,0),(2,1))

(1to10).filter(x=>isPrime(x))//=>Vector(2,3,5,7)

(1to10).fold(0){(acc,x)=>acc+x}//=>55

Page 39: Loop Like a Functional Programing Native

UseHigh-orderFunctionThesehigh-orderfunctions recursively applycertaincomputationtoacollection

abstractclassList[+T]{defmap[U](f:T=>U):List[U]=thismatch{casex::xs=>f(x)::xs.map(f)caseNil=>Nil}

deffilter(p:T=>Boolean):List[T]=thismatch{casex::xs=>if(p(x))x::xs.filter(p)elsexs.filter(p)caseNil=>Nil}}

Page 40: Loop Like a Functional Programing Native

UseHigh-orderFunctionThesehigh-orderfunctions recursively applycertaincomputationtoacollection

abstractclassList[+T]{defmap[U](f:T=>U):List[U]=thismatch{casex::xs=>f(x)::xs.map(f)caseNil=>Nil}

deffilter(p:T=>Boolean):List[T]=thismatch{casex::xs=>if(p(x))x::xs.filter(p)elsexs.filter(p)caseNil=>Nil}}

Theactualimplementationare different fromthissimpliedversioninorderto:

makethemapplytoarbitrarycollections,notjustlists

makethemtail-recursiveonlists

Page 41: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Findthelongestword(assumethereisONLYone)

valwords=List("way","jam","complain","second-hand","elite")

//AssumethismethodisgivendeffindLongerWord(w1:String,w2:String)={...}

Page 42: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Findthelongestword(assumethereisONLYone)

valwords=List("way","jam","complain","second-hand","elite")

//AssumethismethodisgivendeffindLongerWord(w1:String,w2:String)={...}

//TailRecursiondeffindLongestWord(words:List[String]):String={@tailrecdefgo(words:List[String],longestWord:String)=wordsmatch{caseNil=>longestWordcaseword::otherWords=>go(otherWords,findLongerWord(longestWord,word))}go(words,"")}

Page 43: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Findthelongestword(assumethereisONLYone)

valwords=List("way","jam","complain","second-hand","elite")

//AssumethismethodisgivendeffindLongerWord(w1:String,w2:String)={...}

//UseHigh-orderFunctiondeffindLongestWord(words:List[String]):String=words.fold(""){(acc,x)=>findLongerWord(acc,x)}

Page 44: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Findthelongestword(assumethereisONLYone)

valwords=List("way","jam","complain","second-hand","elite")

//AssumethismethodisgivendeffindLongerWord(w1:String,w2:String)={...}

//UseHigh-orderFunctiondeffindLongestWord(words:List[String]):String=words.fold(""){(acc,x)=>findLongerWord(acc,x)}

deffindLongestWord(words:List[String]):String=words.fold("")(findLongerWord)

Page 45: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Getthenameofemployees

whoworkatacompanyw/morethan5millionrevenue

whoworkatthemarketingdepartment

Page 46: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Getthenameofemployees

whoworkatacompanyw/morethan5millionrevenue

whoworkatthemarketingdepartment

//Herearethedatamodel//`caseclass`isStruct(kindof)inScalacaseclassEmployee(name:String)caseclassDepartment(name:String,employees:List[Employee])caseclassCompany(name:String,revenue:BigInt,departments:List[Department])

//Association//Acompanyhasmanydepartments//Adepartmenthasmanyemployees

Page 47: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Getthenameofemployees

whoworkatacompanyw/morethan5millionrevenue

whoworkatthemarketingdepartment

valcompanies=loadDataFromDatabase()

//Worksbutnotveryreadablevalres=companies.filter(company=>company.revenue>5000000).flatMap(company=>company.departments.filter(d=>d.name=="marketing").flatMap(d=>d.employees.map(e=>e.name)))

Page 48: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Getthenameofemployees

whoworkatacompanyw/morethan5millionrevenue

whoworkatthemarketingdepartment

//Easiertoreadbut//thishelperfunctionistoospecifictobereuseddefgetMarketingEmployeeName(company:Company):List[String]={company.departments.filter(d=>d.name=="marketing").flatMap(d=>d.employees.map(e=>e.name))}

valres=companies.filter(company=>company.revenue>5000000).flatMap(company=>getMarketingEmployeeName(company))

Page 49: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Getthenameofemployees

whoworkatacompanyw/morethan5millionrevenue

whoworkatthemarketingdepartment

//Morereadablebutlessefficient-//createunnecessaryintermediateresultvalres=companies.filter(c=>c.revenue>5000000).flatMap(c=>c.departments).filter(d=>d.name=="marketing").flatMap(d=>d.employees).map(e=>e.name)

Page 50: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Getthenameofemployees

whoworkatacompanyw/morethan5millionrevenue

whoworkatthemarketingdepartment

//Asefficientasthe1st/2ndoneand//asreadableasthe3rdonefor{c<-companiesifc.revenue>5000000d<-c.departmentsifd.name=="marketing"e<-d.employees}yielde.name

Page 51: Loop Like a Functional Programing Native

UseHigh-orderFunctionQuestion:Getthenameofemployees

whoworkatacompanyw/morethan5millionrevenue

whoworkatthemarketingdepartment

//Asefficientasthe1st/2ndoneand//asreadableasthe3rdonefor{c<-companiesifc.revenue>5000000d<-c.departmentsifd.name=="marketing"e<-d.employees}yielde.name

Forexpressionwillbeconvertedintoasetof map , flatMap , withFilter (or filter )operations.

Page 52: Loop Like a Functional Programing Native

NOTE:SkipLazyEvaluationandInfiniteListifprevsectiontakestoolong

Page 53: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:Findnthprimenumber

nthPrime(0)//=>2nthPrime(1)//=>3nthPrime(5)//=>13

Page 54: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:Findnthprimenumber

nthPrime(0)//=>2nthPrime(1)//=>3nthPrime(5)//=>13

//Imperative//Thissolutioniserror-pronedefnthPrime(n:Int):Int={vark=2varcounter=0

while(counter<n){k+=1if(isPrime(k)){counter+=1}}k}

Page 55: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:Findnthprimenumber

nthPrime(0)//=>2nthPrime(1)//=>3nthPrime(5)//=>13

//Usehigher-orderfunctiondefnthPrime(n:Int,upperBound:Int):Int={(1toupperBound).filter(isPrime)(n)}

Page 56: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:Findnthprimenumber

nthPrime(0)//=>2nthPrime(1)//=>3nthPrime(5)//=>13

But...howdoweknowthevalueof upperBound

Page 57: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:Findnthprimenumber

nthPrime(0)//=>2nthPrime(1)//=>3nthPrime(5)//=>13

But...howdoweknowthevalueof upperBound

//`upperBound`istoosmall=>exceptionnthPrime(50,100)

//`upperBound`istoolarge=>runforevernthPrime(50,Int.MaxValue)

//`upperBound`hastheperfectvalue,whichwillbetheansweritselfnthPrime(50,229)

Page 58: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:Findnthprimenumber

nthPrime(0)//=>2nthPrime(1)//=>3nthPrime(5)//=>13

//http://stackoverflow.com/questions/13206552/scala-rangex-int-maxvalue-vs-stream-fromx//UseStreamforLazyEvaluationdefnthPrime(n:Int):Int=primeNums(n)

//AstreamofprimenumberdefprimeNums:Stream[Int]=Stream.from(2).filter(isPrime)

primeNums.take(3)//=>Stream(2,?)primeNums.take(3).toList//=>List(2,3,5)

Page 59: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:Getthenthfibnumber

//Assumen>0deffib(n:Int):Int={if(n==0)1elseif(n==1)1elsefib(n-1)+fib(n-2)}

deffib(n:Int):Int={deffibNums(n1:Int,n2:Int):Stream[Int]={n1#::fibNums(n2,n1+n2)}fibNums(1,1)(n)}

Page 60: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:CreatetheprimenumbersequenceusingSieveofEratosthenes

defsieve(s:Stream[Int]):Stream[Int]=s.head#::sieve(s.tailfilter(_%s.head!=0))

valprimes=sieve(Stream.from(2))

primes.take(5).toList//=>List(2,3,5,7,11)

Page 61: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:Howtoimplement sqrt methodrecursively

defsqrtStream(x:Double):Stream[Double]={defimprove(guess:Double)=(guess+x/guess)/2//TODO:Whatifno`lazy`lazyvalguesses:Stream[Double]=1#::(guessesmapimprove)guesses}

Page 62: Loop Like a Functional Programing Native

LazyEvaluationandInfiniteListQuestion:Howtoimplement sqrt methodrecursively

defsqrtStream(x:Double):Stream[Double]={defimprove(guess:Double)=(guess+x/guess)/2//TODO:Whatifno`lazy`lazyvalguesses:Stream[Double]=1#::(guessesmapimprove)guesses}

sqrtStream(4).take(8).toList//=>List(1.0,2.5,2.05,2.000609756097561,2.0000000929222947,2.000000000000002,2.0,2.0)

defisGoodEnough(guess:Double,x:Double)=math.abs((guess*guess-x)/x)<0.0001

sqrtStream(4).filter(isGoodEnough(_,4)).take(1).head//=>2.0000000929222947

Coursera:FunctionalProgramingDesigninScala

Page 63: Loop Like a Functional Programing Native

SummaryHerearetheFunctionalProgramingapproachesthatreplacestheimperative for or while loop:

Recursion

TailRecursion

High-orderFunction

LazyEvalandInfiniteList