国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

惰性求值/Call by name

2018-02-24 15:59 更新

維基百科中惰性求值的解釋
惰性求值(Lazy Evaluation),又稱惰性計算、懶惰求值,是一個計算機編程中的一個概念,它的目的是要最小化計算機要做的工作。它有兩個相關(guān)而又有區(qū)別的含意,可以表示為“延遲求值”和“最小化求值”,本條目專注前者,后者請參見最小化計算條目。除可以得到性能的提升外,惰性計算的最重要的好處是它可以構(gòu)造一個無限的數(shù)據(jù)類型。
惰性求值的相反是及早求值,這是一個大多數(shù)編程語言所擁有的普通計算方式。

惰性求值不是新鮮事

import scala.io.Source.fromFile
val iter: Iterator[String] =
  fromFile("sampleFile")
    .getLines()

文件迭代器就用到了惰性求值.
用戶可以完全像操作內(nèi)存中的數(shù)據(jù)一樣操作文件,然而文件只有一小部分傳入了內(nèi)存中.

用lazy關(guān)鍵詞指定惰性求值

lazy val firstLazy = {
  println("first lazy")
  1
}
lazy val secondLazy = {
  println("second lazy")
  2
}?
def add(a:Int,b:Int) = {
  a+b
}
//在 scala repl 中的結(jié)果
scala> add(secondLazy,firstLazy)
second lazy
first lazy
res0: Int = 3

res0: Int = 3

second lazy 先于 first lazy輸出了

Call by value 就是函數(shù)參數(shù)的惰性求值

def firstLazy = {
  println("first lazy")
  1
}
def secondLazy = {
  println("second lazy")
  2
}
def chooseOne(first: Boolean, a: Int, b: Int) = {
  if (first) a else b
}
def chooseOneLazy(first: Boolean, a: => Int, b: => Int) = {
  if (first) a else b
}
chooseOne(first = true, secondLazy, firstLazy)
//second lazy
//first lazy
//res0: Int = 2
chooseOneLazy(first = true, secondLazy, firstLazy)
//second lazy
//res1: Int = 2

對于非純函數(shù),惰性求值會產(chǎn)生和立即求值產(chǎn)生不一樣的結(jié)果.

一個例子,假設(shè)你要建立一個本地緩存

//需要查詢mysql等,可能來自于一個第三方j(luò)ar包
def itemIdToShopId: Int => Int ?
var cache = Map.empty[Int, Int]
def cachedItemIdToShopId(itemId: Int):Int = {
  cache.get(itemId) match {
    case Some(shopId) => shopId
    case None =>
      val shopId = itemIdToShopId(itemId)
      cache += itemId -> shopId
      shopId
  }
}
  • 羅輯沒什么問題,但測試的時候不方便連mysql怎么辦?
  • 如果第三方j(luò)ar包發(fā)生了改變,cachedItemIdToShopId也要發(fā)生改變.
//用你的本地mock來測試程序
def mockItemIdToSHopId: Int => Int
def cachedItemIdToShopId(itemId: Int): Int ={  
  cache.get(itemId) match { 
    case Some(shopId) => shopId
   case None => 
      val shopId = mockItemIdToSHopId(itemId)
      cache += itemId -> shopId
     shopId 
  } 
}   
  • 在測試的時候用mock,提交前要換成線上的,反復(fù)測試的話要反復(fù)改動,非常令人沮喪.
  • 手工操作容易忙中出錯.
//將遠程請求的結(jié)果作為函數(shù)的一個參數(shù)
def cachedItemIdToShopId(itemId: Int, remoteShopId: Int): Int = {   
  cache.get(itemId) match { 
    case Some(shopId) => shopId 
    case None =>    
     val shopId = remoteShopId  
     cache += itemId -> shopId  
      shopId
  } 
}
//調(diào)用這個函數(shù)
cachedItemIdToShopId(itemId,itemIdToShopId(itemId))
  • 函數(shù)對mysql的依賴沒有了
  • 不需要在測試和提交時切換代碼
  • 貌似引入了新問題?

沒錯,cache根本沒有起應(yīng)有的作用,函數(shù)每次執(zhí)行的時候都調(diào)用了itemIdToShopId從遠程取數(shù)據(jù)

//改成call by name就沒有這個問題啦
def cachedItemIdToShopId(itemId: Int, remoteShopId: =>Int): Int = { 
  cache.get(itemId) match { 
    case Some(shopId) => shopId 
    case None =>    
     val shopId = remoteShopId  
     cache += itemId -> shopId  
      shopId
  } 
}
//調(diào)用這個函數(shù)
cachedItemIdToShopId(itemId,itemIdToShopId(itemId))
  • 函數(shù)對mysql的依賴沒有了
  • 不需要在測試和提交時切換代碼
  • 只在需要的時候查詢遠程庫
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號