如何通过删除特定的分隔(Haskell)将列表拆分为列表

时间:2013-08-25 20:30:11

标签: list haskell split declarative

我是Haskell的新手,我有一个问题。我需要编写一个函数,将列表拆分为列表中的“分离”。

3 个答案:

答案 0 :(得分:7)

我将尝试帮助您了解如何通过递归开发在列表上工作的函数。了解如何首先以“低级”方式进行操作是有帮助的,这样您就可以更好地理解实际代码中更常见的“高级”方式。

首先,您必须考虑要使用的数据类型的性质。该列表在某种意义上是Haskell中递归定义类型的规范示例:列表是空列表[]或者是列表元素a与列表a : list组合在一起}。这是唯一的两种可能性。我们将空列表称为基本情况,因为它是在其定义中不引用自身的那个。如果没有基本情况,递归将永远不会“触底”并且会无限期地继续下去!

列表定义中有两种情况意味着您必须在使用列表的函数定义中考虑两种情况。在Haskell中考虑多个案例的规范方法是模式匹配。 Haskell语法提供了许多方式来进行模式匹配,但我现在只使用基本的case表达式:

case xs of
  []    ->  ...
  x:xs' ->  ...

这是列表必须考虑的两种情况。第一个匹配文字空列表构造函数;第二个匹配元素添加构造函数:,并将两个变量xxs'绑定到列表中的第一个元素和包含其余元素的子列表。

如果你的函数传递了一个与第一个案例匹配的列表,那么你知道初始列表是空的,或者你已经完成了列表中的最后一个元素的递归。无论哪种方式,都没有更多的清单要处理;你要么完成了(如果你的调用是尾递归的),要么你需要将你的答案结构的基本元素传递给调用它的函数(通过返回它)。如果您的答案是列表,则基本元素通常会再次为空列表[]

如果你的函数被传递了一个与第二种情况相匹配的列表,那么你知道它被传递了一个非空列表,而且你还有几个新变量绑定到有用的值。基于这些变量,您需要决定两件事:

  1. 如果我在列表的 rest 上执行正确的答案,我如何对该元素执行算法的一步?
  2. 如何将该步骤的结果与在列表其余部分执行该结果的结果相结合?
  3. 一旦你找到了这些问题的答案,你需要构建一个结合它们的表达式;获得列表其余部分的答案只是在列表的其余部分调用递归调用,然后您需要执行第一个元素和组合的步骤。

    这是一个查找列表长度的简单示例

    listLength :: [a] -> Int
    listLength as =
      case as of
        []    -> 0                  -- The empty list has a length of 0
        a:as' -> 1 + listlength as' -- If not empty, the length is one more than the
                                    -- length of the rest of the list
    

    这是从列表

    中删除匹配元素的另一个示例
    listFilter :: Int -> [Int] -> Int
    listFilter x ns =
      case ns of
        []    -> []                            -- base element to build the answer on
        n:ns' -> if n == x
                   then listFilter x ns'       -- don't include n in the result list
                   else n : (listFilter x ns') -- include n in the result list
    

    现在,您提出的问题有点困难,因为它涉及一个辅助“列表匹配”递归,以识别列表中基本递归中的分隔符。为递送函数添加额外参数有时很有帮助,以便保存有关问题所在位置的额外信息。也可以通过将两个参数放在一个元组中来对两个参数进行模式匹配:

    case (xs, ys) of
      ([]   , []   ) -> ...
      (x:xs', []   ) -> ...
      ([]   , y:ys') -> ...
      (x:xs', y:ys') -> ...
    

    希望这些提示可以帮助您在问题上取得一些进展!

答案 1 :(得分:3)

让我们看看问题是否能够以明显的方式减少。

假设调用splitList时使用xs将split和ys作为分隔符。如果xs为空,问题是最小的,那么这个问题的答案是什么?在这里得到正确答案很重要,因为归纳解决方案取决于这个决定。但我们可以稍后做出这个决定。

好的,因此要使问题可以减少,列表xs不为空。因此,它至少有一个头元素h和较小的问题t,列表的尾部:你可以匹配xs @(h:t)。如何获得较小问题的解决方案?好吧,splitList可以通过函数的定义来解决这个问题。所以现在的诀窍是找出如何为更大的问题(h:t)构建解决方案,当我们知道解决较小问题的解决方案时zs = splitList t ys。在这里我们知道zs是列表列表,[[a]],并且因为t可能是最小的问题,所以zs可能是最小问题的解决方案。所以,无论你用zs做什么,它都必须是有效的,即使对于最小问题的解决方案也是如此。

  splitList [] ys = ... -- some constant is the solution to the smallest problem
  splitList xs@(h:t) ys = let zs = splitList t ys
                          in ... -- build a solution to (h:t) from solution to t

答案 2 :(得分:1)

  

我不知道如何测试它。有人告诉我如何将函数写入.hs文件并使用winGHCi来运行此函数吗?

WinGHCi自动与.hs文件关联,因此只需双击该文件即可启动ghci。使用您喜欢的编辑器对文件进行一些更改后,您可以使用ghci中的:r命令重新加载文件。

要在修复拼写错误,输入错误并确保正确缩进后测试程序,请尝试调用使用不同输入定义的函数(或使用QuickCheck)。注意Maybe定义为Just xNothing。您可以使用fromMaybe提取x(并为Nothing案例提供默认值)。

还要尽量确保模式匹配是详尽无遗的。