避免在 If Else 中重复调用 Scala 中的同一函数

发布于 2025-01-17 00:14:13 字数 1625 浏览 0 评论 0原文

我有以下我想优化的代码片段:

import org.joda.time.{DateTime, Days, Months, Weeks, Years}

  @scala.annotation.tailrec
  def optimize(start: DateTime, end: DateTime, acc: List[(DateTime, DateTime)]): List[(DateTime, DateTime)] = {

    def doDaysExist(startDate: DateTime, endDate: DateTime): Boolean = {
      Days.daysBetween(startDate, endDate).getDays != 0
    }

    if (Years.yearsBetween(start, end.plusDays(1)).getYears != 0) {
      val years = Years.yearsBetween(start, end.plusDays(1)).getYears
      val newStart = start.plusYears(years).minusDays(1)
      if (doDaysExist(newStart, end)) {
        optimize(newStart, end, (start, newStart) +: acc)
      } else (start, end) +: acc
    } else if (Months.monthsBetween(start, end.plusDays(1)).getMonths != 0) {
      val months = Months.monthsBetween(start, end.plusDays(1)).getMonths
      val newStart = start.plusMonths(months).minusDays(1)
      if (doDaysExist(newStart, end)) {
        optimize(newStart, end, (start, newStart) +: acc)
      } else (start, end) +: acc
    } else if (Weeks.weeksBetween(start, end.plusDays(1)).getWeeks != 0) {
      val weeks = Weeks.weeksBetween(start, end.plusDays(1)).getWeeks
      val newStart = start.plusWeeks(weeks).minusDays(1)
      if (doDaysExist(newStart, end)) {
        optimize(newStart, end, (start, newStart) +: acc)
      } else (start, end) +: acc
    } else {
      // For sure days
      (start, end) +: acc
    }
  }

可以看出,if 条件和后面的下一个语句是重复的代码。有没有更好的方法让它变得更好、更神秘?我必须逐步遍历这个层次结构,因为我必须首先检查年份,然后是月份,然后是星期,然后是日期。

我可以考虑将重复的代码块提取到 lambda 中,但这足够好吗?我无法考虑将其重写为 Map,因为该代码块在尾递归函数中运行。

I have the following code snippet that I would like to optimize:

import org.joda.time.{DateTime, Days, Months, Weeks, Years}

  @scala.annotation.tailrec
  def optimize(start: DateTime, end: DateTime, acc: List[(DateTime, DateTime)]): List[(DateTime, DateTime)] = {

    def doDaysExist(startDate: DateTime, endDate: DateTime): Boolean = {
      Days.daysBetween(startDate, endDate).getDays != 0
    }

    if (Years.yearsBetween(start, end.plusDays(1)).getYears != 0) {
      val years = Years.yearsBetween(start, end.plusDays(1)).getYears
      val newStart = start.plusYears(years).minusDays(1)
      if (doDaysExist(newStart, end)) {
        optimize(newStart, end, (start, newStart) +: acc)
      } else (start, end) +: acc
    } else if (Months.monthsBetween(start, end.plusDays(1)).getMonths != 0) {
      val months = Months.monthsBetween(start, end.plusDays(1)).getMonths
      val newStart = start.plusMonths(months).minusDays(1)
      if (doDaysExist(newStart, end)) {
        optimize(newStart, end, (start, newStart) +: acc)
      } else (start, end) +: acc
    } else if (Weeks.weeksBetween(start, end.plusDays(1)).getWeeks != 0) {
      val weeks = Weeks.weeksBetween(start, end.plusDays(1)).getWeeks
      val newStart = start.plusWeeks(weeks).minusDays(1)
      if (doDaysExist(newStart, end)) {
        optimize(newStart, end, (start, newStart) +: acc)
      } else (start, end) +: acc
    } else {
      // For sure days
      (start, end) +: acc
    }
  }

As it can be seen that the if condition and the next statement that follows are kind of duplicate code. Is there a better way to make it better and cryptic? I have to step through this hierarchy as I have to first check the year, then the month, then the week and then the day.

I can think of extracting the repeated code block into lambdas, but is that good enough? I can't think of rewriting it as a Map as that code block runs in a tail recursive function.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦里泪两行 2025-01-24 00:14:13

像这样的事情怎么样:

def optimize(start: DateTime, end: DateTime, acc: List[(DateTime, DateTime)]): List[(DateTime, DateTime)] = {
  val endPlusDay = end.plusDays(1)
  
  val checks = List(
    {
      val years = Years.yearsBetween(start, endPlusDay).getYears
      val newStart = start.plusYears(years).minusDays(1)
 
      (years -> newStart)
    },
    {
      val months = Months.monthsBetween(start, endPlusDay).getMonths
      val newStart = start.plusMonths(months).minusDays(1)
 
      (months -> newStart)
    },
    {
      val weeks = Weeks.weeksBetween(start, endPlusDay).getWeeks
      val newStart = start.plusWeeks(weeks).minusDays(1)
 
      (weeks -> newStart)
    }
  )
  
  checks.collectFirst {
    case (difference, newStart) if (
      (difference != 0) && (Days.daysBetween(newStart, end) != 0)
    ) =>
      optimize(newStart, end, acc :+ (start, newStart))
  }.getOrElse(acc :+ (start, end))
}

PS:两个建议

  1. 使用java.time而不是Joda
  2. 前置并最后反向到累加器而不是追加。

What about something like this:

def optimize(start: DateTime, end: DateTime, acc: List[(DateTime, DateTime)]): List[(DateTime, DateTime)] = {
  val endPlusDay = end.plusDays(1)
  
  val checks = List(
    {
      val years = Years.yearsBetween(start, endPlusDay).getYears
      val newStart = start.plusYears(years).minusDays(1)
 
      (years -> newStart)
    },
    {
      val months = Months.monthsBetween(start, endPlusDay).getMonths
      val newStart = start.plusMonths(months).minusDays(1)
 
      (months -> newStart)
    },
    {
      val weeks = Weeks.weeksBetween(start, endPlusDay).getWeeks
      val newStart = start.plusWeeks(weeks).minusDays(1)
 
      (weeks -> newStart)
    }
  )
  
  checks.collectFirst {
    case (difference, newStart) if (
      (difference != 0) && (Days.daysBetween(newStart, end) != 0)
    ) =>
      optimize(newStart, end, acc :+ (start, newStart))
  }.getOrElse(acc :+ (start, end))
}

PS: Two recommendations

  1. Use java.time instead of Joda.
  2. Prepend and finally reverse to the accumulator instead of append.
小…红帽 2025-01-24 00:14:13

只需预先计算值并将第二个测试移到 if/else 之外:

val years = Years.yearsBetween(start, end.plusDays(1)).getYears
lazy val months = Months.monthsBetween(start, end.plusDays(1)).getMonths
lazy val weeks = Weeks.weeksBetween(start, end.plusDays(1)).getWeeks

val newStart =
  if (years != 0) {
    start.plusYears(years).minusDays(1)
  } else if (months != 0) {
    start.plusMonths(months).minusDays(1)
  } else if (weeks != 0) {
    start.plusWeeks(weeks).minusDays(1)
  } else {
    start
  }

if (doDaysExist(newStart, end)) {
  optimize(newStart, end, (start, newStart) +: acc)
} else {
  (start, end) +: acc
}

使用lazy 可能会也可能不会提高性能,因此请衡量以查看哪个是最好的。

Just pre-compute the values and move the second test outside the if/else:

val years = Years.yearsBetween(start, end.plusDays(1)).getYears
lazy val months = Months.monthsBetween(start, end.plusDays(1)).getMonths
lazy val weeks = Weeks.weeksBetween(start, end.plusDays(1)).getWeeks

val newStart =
  if (years != 0) {
    start.plusYears(years).minusDays(1)
  } else if (months != 0) {
    start.plusMonths(months).minusDays(1)
  } else if (weeks != 0) {
    start.plusWeeks(weeks).minusDays(1)
  } else {
    start
  }

if (doDaysExist(newStart, end)) {
  optimize(newStart, end, (start, newStart) +: acc)
} else {
  (start, end) +: acc
}

Using lazy may or may not improve performance so measure to see which is best.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文