【问题标题】:Counting the times of recursion in Haskell在 Haskell 中计算递归的次数
【发布时间】:2019-06-10 19:03:00
【问题描述】:

我正在用 Haskell 写一个小学校作业,以确定两个给定日期之间的距离。我编写了一个粗略的函数来循环日期,但我无法理解如何以函数式编程方式编写循环。我以前只做过程序和 OOP 编程。我不知何故需要存储我调用 nextDate 函数的次数的信息,但 Haskell 不允许我在函数中引入变量。这是我到目前为止提出的代码。这根本不是 Haskelly...

nextDate year month day = 
    if day + 1 < 31
        then (year,month, day+1)
    else if month + 1 < 12
        then (year, month + 1, 1)
    else (year +1,1,1)

calculateDifference year month day year2 month2 day2 = 
    let x = 0
    if year == year2 && month == month2 && day == day2 then x
    else 
     nextDate(year, month, day)
     x = x + 1

    -- How I would do it in Python
    -- x = 0
    -- while((tuple1) != (year2, month2, day2)):
    --  x += 1
    --  tuple1 = nextDate(tuple1)
    -- print(x)

【问题讨论】:

标签: loops haskell recursion counting


【解决方案1】:

如果您想跟踪函数被调用的次数,您需要将其作为输入提供。没有其他方法可以做到这一点,因为 Haskell 只允许您使用传递给函数的参数。

例如,假设我想计算一个阶乘,但我想跟踪它花了多少步。我的函数签名可能如下所示:

factorial :: Int -> (Int, Int) -- Takes a number, returns the number and recursion count
factorialInternal :: (Int, Int) -> (Int, Int) -- This actually does the recursion

然后定义可能如下所示:

factorial n = factorialInternal (n, 0)
factorialInternal (1, n) = (1, n + 1)
factorialInternal (x, n) = let (y, z) = factorialInternal (x-1, n) in (x * y, z + 1)

本质上,跟踪递归量的参数在每一级递增,然后成为factorial输出的一部分。

创建一个接口函数绝对有帮助,这样您在使用该函数时就不必手动输入起始递归级别(无论如何,它总是为零)。您的函数签名可能如下所示的示例:

-- The function you call
calculateDifference :: (Int, Int, Int) -> (Int, Int, Int) -> Int
-- What the calculateDifference function calls (the third parameter is the recursion counter)
calculateDifferenceInternal :: (Int, Int, Int) -> (Int, Int, Int) -> Int -> Int

从这里,你应该能够弄清楚如何实现calculateDifferencecalculateDifferenceInternal


编辑:正如 amalloy 所指出的,更好的解决方案是只输出计数器,而不是输入计数器:所以 factorialInternal :: (Int, Int) -&gt; (Int, Int) 代替 factorialInternal Int -&gt; (Int, Int) 会起作用。定义将如下所示:

factorialInternal 1 = (1, 0)
factorialInternal n = let (x, y) = factorialInternal (n - 1) in (n * x, y + 1)

【讨论】:

  • “没有其他方法可以做到这一点” - 不完全是:你可以用新的输出而不是新的输入来做到这一点。在基本情况下,为您的计数器返回 0,在递归情况下,将递归调用产生的结果加 1。
  • 你说得对,这是解决这个问题的另一种方法。我将编辑我的答案并将其添加进去。
  • 我不会说我的建议更好。它是不同的,并且对于某些功能会表现更好,但对于其他功能会更差。很高兴了解两者。
猜你喜欢
  • 2015-05-18
  • 1970-01-01
  • 1970-01-01
  • 2012-05-24
  • 1970-01-01
  • 2021-07-24
  • 2014-06-08
  • 1970-01-01
  • 2022-01-06
相关资源
最近更新 更多