函數(shù)式編程更偏向于輸入和輸出
interface A{
piblice boolean apply(T input);
piblice boolean equals(Object other);
}
轉(zhuǎn)化scala
用 T => Boolean 表示(是不是很神奇)
表達式:有返回值
語句:可執(zhí)行,但無返回值
scala中 if 就是一個表達式
scala 大部分語句是返回最后一個表達式的值作為結(jié)果
1、Functor 函子T[_] (態(tài)射)
面向函數(shù)編程 其實 講的是一種范疇論,即一種轉(zhuǎn)化,從一種范疇轉(zhuǎn)化到另一種范疇,用態(tài)射表示其中的映射關(guān)系 例如 flatmap和map。
T[_]:
貓[黃色]? ? 貓[紅色]
狗[黃色]? ? 狗[紅色]
貓 -》 狗? ==== 函子
黃色 -》 紅色 === 態(tài)射
函子的一個典型應(yīng)用就是 map 和 flapmap(拆包和解包)
2、Applicative
我們把類似Option這一類值當(dāng)作封裝過的值,F(xiàn)unctor可以把這類封裝過的值拆包,并執(zhí)行函數(shù)映射。但是,如果 Function 也是被包裝過的,F(xiàn)unctor還能發(fā)揮作用嗎?看一個實驗:

我們目的是要遍歷 op1,讓其內(nèi)部執(zhí)行 (x:Int) => x * x,也就是把 op1的 數(shù)值1 拿出來執(zhí)行 1 * 1 = 1
op1.map(fop _) 報錯了,通過代碼也可以看出這是不可行的。而 fop 是一個Option類型,如果執(zhí)行內(nèi)部函數(shù)應(yīng)該使用map方法
scala> op1.map(o=>fop.map(f => o))
res2: Option[Option[Int]] = Some(Some(1))
如上雖然通過map函數(shù)可以執(zhí)行到函數(shù)但是返回值卻不是我們期望的:Option[Option[Int]]。
應(yīng)該返回 1 Int類型 。OK,Applicative出場。
Applicative的作用是: 某個值去調(diào)用封裝函數(shù)里的函數(shù),如下:
scala> val f1 = (x: Int) => x + 1
f1: Int => Int =
scala> val f2 = (x: Int) => x + 2
f2: Int => Int =
scala> val fs = List(f1, f2)? // 對 f1 和 f2 進行封裝
fs: List [Int => Int] = List(, )
scala> for(f <- fs; x <- List(1, 2, 3)) yield f(x)
res15: List[Int] = List(2, 3, 4, 3, 4, 5)
List(f1, f2) 映射 List(1, 2, 3),映射之后得到了List(2, 3, 4, 3, 4, 5)
即 封裝過的函數(shù) List(f1, f2) 返回了 傳一個值,返回了 調(diào)用 f1函數(shù) 和 f2函數(shù)過后的值 (當(dāng)然也可以加case 模式達到只調(diào)用一個函數(shù)的目的) 。
3、Monad 單子 ,我理解的就是一個封裝了某種行為的一個函數(shù),我們可以將它傳來傳去。
Monad的典型應(yīng)用就是flatMap,它封裝了壓縮操作
比如 List( List(1), List(2) ) flat 之后是 List(1, 2)
Some( Some(2) ) flat之后是 Some(2)
=================================================================
下面的例子實現(xiàn)的功能:創(chuàng)建一個工作流,先讀入一個文件,做一些計算,然后寫出計算結(jié)果
展示了,函子、單子、和 for(工作流---即代碼從上到下執(zhí)行)。
// 注:如果一個類是函子或單子,那就可以用for表達式來操作里面的類型
//我們可以封裝某代碼片段,定義為一類對象,然后傳來傳去,
// 或者用某種依賴注入方法,注入到框架。
// 例如,當(dāng)我們需要一個類似 依次執(zhí)行的管道而不立即執(zhí)行的的行為時,使用單子是非常有效的??梢杂脝巫觼砜刂坪图s束該行為
import scalax.functional.{Applicative,Functor,Monad}
trait ManagedResource[T] {
? // 在單子里用到了懶加載,就是說單子只返回了一個含有某種操作的對象,當(dāng)調(diào)用loan函數(shù)時才懶加載才觸發(fā)
? def loan[U](f: T => U): U
}
// 文件讀、寫操作的封裝
object ManagedResource {
? def readFile(file: java.io.File) = new ManagedResource[java.io.InputStream] {
? ? def loan[U](f: java.io.InputStream => U): U = {
? ? ? val stream = new java.io.BufferedInputStream(new java.io.FileInputStream(file))
? ? ? try {
? ? ? ? f(stream)// 有括號,函數(shù)調(diào)用
? ? ? } finally {
? ? ? ? stream.close()
? ? ? }
? ? }
? }
? def writeFile(file: java.io.File) = new ManagedResource[java.io.OutputStream] {
? ? def loan[U](f: java.io.OutputStream => U): U = {
? ? ? val stream = new java.io.BufferedOutputStream(new java.io.FileOutputStream(file))
? ? ? try {
? ? ? ? f(stream)
? ? ? } finally {
? ? ? ? stream.close()
? ? ? }
? ? }
? }
? def make[T <: { def close(): Unit }](t: => T): ManagedResource[T] =
? ? new ManagedResource[T] {
? ? ? def loan[U](f: T => U): U = {
? ? ? ? val resource = t
? ? ? ? try {
? ? ? ? ? f(resource)
? ? ? ? } finally {
? ? ? ? ? resource.close()
? ? ? ? }
? ? ? }
? ? ? override def toString = "ManagedResource(...)"
? ? }
? // 有了函子和單子的隱式實現(xiàn),scala就可以在上下文中找到它
? // 函子的apply方法:當(dāng)調(diào)用loan(借給)方法時,把a “ 借用給 f”
? // 函子的map方法:當(dāng)調(diào)用loan(借給)方法時,即調(diào)用ma的租借方法,參數(shù)數(shù) 用mapping包裝f后的值
? implicit object MRFunctor extends Functor[ManagedResource] {
? ? override def apply[A](a: A) = new ManagedResource[A] {
? ? ? override def loan[U](f: A => U) = f(a)
? ? ? override def toString = "ManagedResource("+a+")"
? ? }
? ? override def map[A,B](ma: ManagedResource[A])(mapping: A => B) = new ManagedResource[B] {
? ? ? override def loan[U](f: B => U) = ma.loan(mapping andThen f)
? ? ? override def toString = "ManagedResource.map("+ma+")("+mapping+")"
? ? }
? }
? // 單子的 flatten 方法實現(xiàn) 先調(diào)用外層資源的 loan 方法,然后再調(diào)用外層資源返回的內(nèi)部資源的 loan 方法
? implicit object MRMonad extends Monad[ManagedResource] {
? ? override def flatten[A](mma: ManagedResource[ManagedResource[A]]): ManagedResource[A] =
? ? ? new ManagedResource[A] {
? ? ? ? override def loan[U](f: A => U): U = mma.loan(ma => ma.loan(f))
? ? ? ? override def toString = "ManagedResource.flatten("+mma+")"
? ? ? }
? }
? implicit object MRApplicative extends Applicative[ManagedResource] {
? ? override def lift2[A,B](func: ManagedResource[A=>B])(ma: ManagedResource[A]): ManagedResource[B] =
? ? ? new ManagedResource[B] {
? ? ? ? override def loan[U](f: B => U): U = func.loan(n => ma.loan(n andThen f))
? ? ? ? override def toString = "ManagedResource.lift2("+func+")("+ma+")"
? ? ? }
? }
}
=========================================================
import scalax.functional.{Applicative, Functor, Monad, Implicits}
import Implicits._
import java.io._
object Example {
? type LazyTraversable[T] = collection.TraversableView[T, Traversable[T]]
? // 函數(shù)是被調(diào)用,所以用接受。
? // makeLineTraversable 方法接受一個input參數(shù),返回一個 Traversable[String]對象
? // foreach 方法調(diào)用readLine,直到全部讀完。每讀出一行,把它喂給匿名函數(shù) f
? // 最后調(diào)用view方法返回惰性求值的文本行 集合(A[B] 表示 B集合)
? // type LazyTraversable[T] = collection.TraversableView[T,Traversable[T]]
? def makeLineTraversable(input: BufferedReader) = new Traversable[String] {
? ? def foreach[U](f: String => U): Unit = {
? ? ? var line = input.readLine()
? ? ? while (line != null) {
? ? ? ? f(line)
? ? ? ? line = input.readLine()
? ? ? }
? ? }
? } view
? // 函數(shù)是被調(diào)用,所以用接受。
? // getLines方法接受一個 file文件對象,返回一個包含字符串的 ManagedResource 集合
? // 用for表達式,表達一個工作流
? // 調(diào)用 readFile 方法,返回 輸入流,并包裝成buffered
? // 最后把 buffered 傳給 makeLineTraversable來構(gòu)造一個 LazyTraversable[T] 返回。
? // 注:如果一個類是函子或單子,那就可以用for表達式來操作里面的類型
? def getLines(file: File): ManagedResource[LazyTraversable[String]] =
? ? for {
? ? ? input <- ManagedResource.readFile(file)
? ? ? val reader = new InputStreamReader(input)
? ? ? val buffered = new BufferedReader(reader)
? ? } yield makeLineTraversable(buffered)??
? // 現(xiàn)在需要逐行讀入,并計算每行的長度,計算結(jié)果寫入一個新文件。
? // 下面定一個新工作流來實現(xiàn)
? // lineLedngthCount方法接受兩個參數(shù),最后將結(jié)果寫入新文件
? // 調(diào)用 getLines 返回所有行的 TraversableView
? // 然后對每一行調(diào)用 length 計算長度,并將結(jié)果與行號組合。然后封裝流
? // 最后 BufferedWriter 寫入新文件
? //--------------------------------------------------------------------------------------------------
? // 注這個方法并不執(zhí)行任何計算,它只是返回一個 ManagedResource[Unit]
? // 這個單子的 loan 方法被調(diào)用時才讀取、計算、并寫入結(jié)果。這個工作流只是組合了計算行長度的行為
? // 而它并不實際執(zhí)行。這樣就帶來了一些靈活性,我們可以將該代碼片段,定義為一類對象,然后傳來傳去,
? // 或者用某種依賴注入方法,注入到框架。
? // 當(dāng)我們需要一個類似 依次執(zhí)行的管道而不立即執(zhí)行的的行為時,使用單子是非常有效的??梢杂脝巫觼砜刂坪图s束該行為
? def lineLengthCount(inFile: File, outFile: File) =
? ? for {
? ? ? lines <- getLines(inFile)
? ? ? val counts = lines.map(_.length).toSeq.zipWithIndex
? ? ? output <- ManagedResource.writeFile(outFile)
? ? ? val writer = new OutputStreamWriter(output)
? ? ? val buffered = new BufferedWriter(writer)
? ? } yield buffered.write(counts.mkString("\n"))
? def main(args: Array[String]): Unit = {
? ? ? workflow(new File("test.in"), new File("test.out")).loan(_ => ())
? ? }
}