簡體   English   中英

是否有一種在Scala中創建尾遞歸函數的通用方法?

[英]Is there a universal method to create a tail recursive function in Scala?

當檢查英特爾的BigDL 回購 ,我偶然發現了這種方法:

private def recursiveListFiles(f: java.io.File, r: Regex): Array[File] = {
  val these = f.listFiles()
  val good = these.filter(f => r.findFirstIn(f.getName).isDefined)
  good ++ these.filter(_.isDirectory).flatMap(recursiveListFiles(_, r))
}

我注意到它不是尾遞歸並且決定編寫一個尾遞歸版本:

private def recursiveListFiles(f: File, r: Regex): Array[File] = {
@scala.annotation.tailrec def recursiveListFiles0(f: Array[File], r: Regex, a: Array[File]): Array[File] = {
  f match {
    case Array() => a
    case htail => {
      val these = htail.head.listFiles()
      val good = these.filter(f => r.findFirstIn(f.getName).isDefined)
      recursiveListFiles0(these.filter(_.isDirectory)++htail.tail, r, a ++ good)
    }
  }
}
recursiveListFiles0(Array[File](f), r, Array.empty[File])
}

與我習慣的相比,這使得難以將File轉換為Array [File]的概念增加了另一個深度。

具有以下成員的數據類型的遞歸背后的理論是什么?

def listTs[T]: T => Traversable[T]

簡短的回答

如果你概括了這個想法並將其視為monad(用於任意類型params的多態性),那么你將無法實現尾遞歸實現。

Trampolines試圖通過提供一種評估遞歸計算而不會溢出堆棧的方法來解決這個問題。 一般的想法是創建一對(結果,計算)的流。 因此,在每個步驟中,您必須將計算結果返回到該點,並使用函數創建下一個結果(也稱為thunk )。

來自Rich Dougherty的博客:

蹦床是一個反復運行功能的循環。 每個函數(稱為thunk)都會返回下一個要運行的循環函數。 蹦床從來不會一次超過一個thunk,所以如果你把你的程序分成足夠小的thunks並從蹦床上跳出來,那么你可以確定堆棧不會變得太大。

更多+參考資料

在分類意義上,這些數據類型背后的理論與Cofree Monadsfoldunfold函數密切相關,並且通常與Fixed point types密切相關。

看看這個精彩的演講:Rob Nris的Fix Cofree和Doobie趣味和游戲,它討論了一個與你的問題非常相似的用例。

這篇關於Free monads和Trampolines的文章也與你的第一個問題有關: Stackless Scala With Free Monads

另見Matryoshka文檔的這一部分。 Matryoshka是一個Scala庫,它圍繞FixedPoint類型的概念實現monad。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM