scala 函子 單子 Functor,Applicative和Monad

函數(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(_ => ())

? ? }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Scala與Java的關(guān)系 Scala與Java的關(guān)系是非常緊密的!! 因為Scala是基于Java虛擬機,也就是...
    燈火gg閱讀 3,607評論 1 24
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,539評論 19 139
  • 一、Python簡介和環(huán)境搭建以及pip的安裝 4課時實驗課主要內(nèi)容 【Python簡介】: Python 是一個...
    _小老虎_閱讀 6,319評論 0 10
  • 前幾天上映的紀(jì)錄片《天梯:蔡國強的天梯》,家附近的影院都沒排,昨天和朋友在藍(lán)港見面,臨時起意跑去最近的一家有排片的...
    膩蟲閱讀 1,121評論 1 1
  • 文/小珞 剛下飛機的我聽到了意外的噩耗,一個黃先生的朋友,算不上摯交的M走了,原因不詳。 因為相處不深,在我印象里...
    小珞的碎碎念閱讀 493評論 4 4

友情鏈接更多精彩內(nèi)容