【问题标题】:Haskell: min distance between neighbor numbers on a listHaskell:列表中相邻数字之间的最小距离
【发布时间】:2014-07-27 07:32:42
【问题描述】:

我正在尝试定义一个函数来查找列表中相邻数字之间的最小距离

类似这样的:

minNeighborsDistance [2,3,6,2,0,1,9,8] => 1

我的代码如下所示:

minNeighborsDistance [] = []
minNeighborsDistance (x:xs) = minimum[minNeighborsDistance xs ++ [subtract x (head xs)]]

虽然这似乎可以运行,但一旦我输入一个列表,我就会收到一个异常错误。

我是 Haskell 的新手,如果能在这件事上提供任何帮助,我将不胜感激。

【问题讨论】:

  • 您在空箱中返回一个列表......而您的示例返回一个数值。您的意思是返回 0 还是 Nothing?似乎你想用一个“neighborsDistance”函数来打破它,你可以应用最小值、最大值等。(例如neighborsDistance [1 2 4] = Just [1 2]neighborsDistance [1] = Nothing(我只是在阅读初学者此刻的 Haskell 问题,刚开始......)
  • 另外一个小提示:如果您可以使用前者,那么使用y : 将元素添加到列表中几乎总是比使用++ [y] 更有效。

标签: haskell


【解决方案1】:

如果你将单例列表传递给minNeighborsDistance 那么

  1. 它将无法匹配第一行中的[],然后
  2. 它会成功匹配(x:xs),将单个值分配给x,将空值分配给xs,然后
  3. 当您尝试访问空列表的head 时会抛出错误。

此外,由于您以递归方式调用 minNeighborsDistance,因此您最终将始终在单例列表中调用它,除非您将其传递给空列表。

【讨论】:

    【解决方案2】:

    这是我想出的:

    minDistance l = minimum . map abs . zipWith (-) l $ tail l
    

    【讨论】:

      【解决方案3】:

      试试这个:

      minDistance list = minimum (distance list)
        where
          distance list = map abs $ zipWith (-) list (tail list)
      

      distance 计算被减去的列表的绝对值,并将其自身移动 1 个位置:

        [2,3,6,2,0,1,9,8] -- the 8 is skipped but it does not make a difference
      - [3,6,2,0,1,9,8]
      = [1,3,4,2,1,8,1]
      

      minDistance 现在只获取结果列表中的最小元素。

      【讨论】:

      • 当你使用abs时,你真的不需要right
      【解决方案4】:

      你的问题有点不清楚(类型签名在这里真的很有帮助),但如果你想计算列表中相邻元素之间的差异,那么找到这些数字中的最小值,我会说最清楚方法是使用一些额外的模式匹配:

      -- Is this type you want the function to have?
      minNeighborsDistance :: [Int] -> Int
      minNeighborsDistance list = minimum $ go list
          where
              go (x:y:rest) = (x - y) : go (y:rest)
              go anythingElse = []        -- Or just go _ = []
      

      但是,这并不能完全给您想要的答案,因为当您从 6 转到 2 时,示例列表的实际最小值将是 -4。但这很容易解决,只需申请abs

      minNeighborsDistance :: [Int] -> Int
      minNeighborsDistance list = minimum $ go list
          where
              go (x:y:rest) = abs (x - y) : go (y:rest)
              go anythingElse = []
      

      我使用了一个辅助函数来计算元素之间的差异,然后顶级定义对该结果调用minimum 以获得最终答案。

      不过,如果您利用Prelude 中的一些函数,即zipWithmapdrop,还有一种更简单的方法:

      minNeighborsDistance :: [Int] -> Int
      minNeighborsDistance list
          = minimum        -- Calculates the minimum of all the distances
          $ (maxBound:)    -- Ensures we have at least 1 number to pass to
                           -- minimum by consing the maximum possible Int
          $ map abs        -- Ensure all differences are non-negative
          -- Compute the difference between each element.  I use "drop 1"
          -- instead of tail because it won't error on an empty list
          $ zipWith (-) list (drop 1 list)
      

      所以在没有cmets的情况下合并成一行:

      minNeighborsDistance list = minimum $ (maxBound:) $ map abs $ zipWith (-) list $ drop 1 list
      

      【讨论】:

      • 我有预感你应该放弃(0:) >:)
      • @ØrjanJohansen 为什么会这样?它处理list 是单例列表的情况。没有它minNeighborsDistance [1] 在空列表上引发异常。
      • @ØrjanJohansen 啊,现在我觉得自己很傻。我们可以改为使用maxBound 来获得我打算的行为......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-31
      • 2021-03-03
      • 2020-01-26
      • 1970-01-01
      • 2021-03-19
      相关资源
      最近更新 更多