【问题标题】:Modify the parameter of a function only once只修改一次函数的参数
【发布时间】:2017-10-01 10:27:19
【问题描述】:

几天前我开始使用 Haskell。 我有以下一段代码(递归函数):

totaltime t [x] = 0
totaltime t route = tbd + totaltime (t+tbd) (tail(route))
    where tbd = timebetweendepartures (fst(route!!0)) (fst(route!!1)) t (snd(route!!1))

“route”是一个元组列表,例如:[(3,1), (1,2), (5,2), (2,1)]

我要做的是在函数开始执行它应该做的事情之前将 (0,0) 添加到路由中。

所以如果参数路由以 [(3,1), (1,2), (5,2), (2,1)] 的形式传递,那么函数应该以 [(0,0) 的形式接收它, (3,1), (1,2), (5,2), (2,1)]

我可以只做totaltime t ((0,0):route),但由于该函数是递归的,因此无法按预期工作。

非常感谢您的想法!

【问题讨论】:

    标签: haskell


    【解决方案1】:

    简短回答:您可以将函数 totaltime 限定为 where 子句,并公开另一个在列表前添加 (0,0) 的函数:

    totaltime t l = tt t ((0,0):l)
    where tt t [x] = 0
          tt t route = tbd + tt (t+tbd) (tail(route))
              where tbd = timebetweendepartures (fst(route!!0)) (fst(route!!1)) t (snd(route!!1))
    

    但话虽如此,代码很不优雅,而且可能不安全:

    • 您没有为空列表定义案例;
    • 用于空列表的情况是第二个子句,您在其中调用!!0!!1,它们是不是total的函数。

    此外,您调用fstsnd 并不是很优雅。我们可以在子句的头部引入通常使其更优雅、更容易理解的模式。因此,我们可以将 tt 函数重写为:

    tt t [] = ... # create a case for the empty list
    tt t [x] = 0
    tt t ((x0,_):xys@((x1,y1):_)) = tbd + tt (t+tbd) xys
        where tbd = timebetweendepartures x0 x1 t y1
    

    所以现在我们可以像这样使用它:

    totaltime t l = tt t ((0,0):l)
        where tt t [] = ... # create a case for the empty list
              tt t [x] = 0
              tt t ((x0,_):xys@((x1,y1):_)) = tbd + tt (t+tbd) xys
                  where tbd = timebetweendepartures x0 x1 t y1
    

    当然,在这种情况下,处理空列表是不可能的,因为totaltime 前置(0,0) 我们保证初始调用是一个至少包含一个元素的调用,并且我们对直接尾部,这意味着列表一次减少一个。

    然而,我们仍然可以通过使用三个参数来改进函数:t 变量;列表的头部,列表的尾部,并重写为:

    totaltime t = tt t (0,0)
        where tt _ _ [] = 0
              tt t (x0,_) (xyh@(x1,y1):xyt) = tbd + tt (t+tbd) xyh xyt
                  where tbd = timebetweendepartures x0 x1 t y1
    

    现在代码在语法上是完整的,而且它可以提高效率(因为我们避免将列表的某些部分解包两次。

    我们仍然可以改进代码。请注意,我们只取列表头部的 first 元素(我们不使用y0 变量)。所以我们可以省略打包和解包并将其重写为:

    totaltime t = tt t 0
        where tt _ _ [] = 0
              tt t x0 ((x1,y1):xyt) = tbd + tt (t+tbd) x1 xyt
                  where tbd = timebetweendepartures x0 x1 t y1

    现在我们节省了拆包元组的工作,而且我们所做的工作更加清晰。

    【讨论】:

    • 您真的需要空列表案例吗?似乎它从至少一个元素(我们添加的那个虚拟元素)开始,并且每次迭代都缩小一个。当然,也许我们可以添加空的情况,然后完全避免包装。
    • @Thilo:我个人更喜欢构建处理所有可能模式的函数(可以抛出error "Should not occur")。但就这样就这样吧,恕我直言不是一个好主意。
    • 我很想走得更远。在最后一次尝试中,没有真正的方法来定义空案例,我们甚至不需要它。我会将第一个列表元素作为另一个参数传递给tt。例如。 tt t (0,0) l 其中tt t x [] = 0 ; tt t (x0,_) xys@(...)=...。 (不过,方向是正确的,所以+1)
    • 另一种选择是使用NonEmpty。这似乎比额外的参数更清晰,并且可能会被优化掉。
    【解决方案2】:

    您可以将该函数包装到一个“入口点”函数中,该函数对外部接收的参数进行预处理:

     totaltime t route =
       totaltime' t ((0,0):route)
       where
          totaltime' t [x] = 0
          totaltime' t route = .....
    

    【讨论】:

      猜你喜欢
      • 2016-06-25
      • 1970-01-01
      • 2017-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-26
      • 1970-01-01
      相关资源
      最近更新 更多