【问题标题】:try/catch not working when use tail recursive function使用尾递归函数时尝试/捕获不起作用
【发布时间】:2020-09-29 08:05:26
【问题描述】:

我正在构建一个尾递归函数,它读取多个 hdfs 路径并将所有这些路径合并到一个数据帧中。只要所有路径都存在,该功能就可以完美运行,如果不存在,则该功能将失败并且不会完成连接确实存在的路径的数据。为了解决这个问题,我尝试使用 try/catch 处理错误,但没有成功。

错误说:无法优化@tailrec注解的方法循环:它包含一个不在尾部位置的递归调用

我的功能是:

def getRangeData(toOdate: String, numMonths: Int, pathRoot: String, ColumnsTable: List[String]): DataFrame = {

    val dataFrameNull = spark.createDataFrame(spark.sparkContext.emptyRDD[Row],
      StructType((ColumnsTable :+ "odate").map(columnName => StructField(columnName, StringType, true))))

    val rangePeriod = getRangeDate(numMonths, toOdate)

    @tailrec
    def unionRangeData(rangePeriod: List[LocalDate], pathRoot: String, df: DataFrame = dataFrameNull): DataFrame = {
      try {
        if (rangePeriod.isEmpty) {
          df
        }
        else {
          val month = "%02d".format(rangePeriod.head.getMonthValue)
          val year = rangePeriod.head.getYear
          val odate = rangePeriod.head.toString

          val path = s"${pathRoot}/partition_data_year_id=${year}/partition_data_month_id=${month}"
          val columns = ColumnsTable.map(columnName => trim(col(columnName)).as(columnName))
          val dfTemporal = spark.read.parquet(path).select(columns: _*).withColumn("odate", lit(odate).cast("date"))

          unionRangeData(rangePeriod.tail, pathRoot, df.union(dfTemporal))
        }
      } catch {
        case e: Exception =>
          logger.error("path not exist")
          dataFrameNull
      }
    }

    unionRangeData(rangePeriod, pathRoot)
  }

  def getRangeDate(numMonths: Int, toOdate: String, listDate: List[LocalDate] = List()): List[LocalDate] = {
    if (numMonths == 0) {
      listDate
    }
    else {
      getRangeDate(numMonths - 1, toOdate, LocalDate.parse(toOdate).plusMonths(1).minusMonths(numMonths) :: listDate)
    }
  }

在此先感谢您的帮助。

【问题讨论】:

  • 在处理 Spark 数据集时,我真的认为担心尾递归没有多大意义。感觉更像是担心火车的胎压。
  • 我不明白你的意思,你自己解释一下
  • 尾递归是一种通过递归函数调用避免不必要的堆栈堆积的技术。大多数 spark 操作(转换)会产生一个表示计算图的 AST,它只是一个数据结构。然后仅在执行消耗操作时优化和计算该计算图。这种计算也发生在分布式环境中。这意味着尾递归的整个概念与 Spark 应用程序并不真正(不应该)相关。
  • 一般来说(在某些情况下可能会有例外),如果您需要担心 Spark 应用程序中的尾递归,那么您的做法非常错误,类似于在火车上装轮胎然后担心轮胎压力。

标签: scala apache-spark


【解决方案1】:

我建议您从函数中完全删除 try-catch 构造,并在 getRangeData 底部的调用站点使用它。

或者,您也可以使用scala.util.Try 包装调用:Try(unionRangeData(rangePeriod, pathRoot)),并使用其中一个组合器来执行您的日志记录或在错误情况下提供默认值。

解释为什么 Scala 编译器不能在 try-catch 中执行尾调用优化的相关文章: Why won't Scala optimize tail call with try/catch?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-31
    • 2012-12-03
    • 1970-01-01
    • 1970-01-01
    • 2018-07-14
    • 2012-01-28
    • 1970-01-01
    相关资源
    最近更新 更多