5

我想做一个程序 insertAt,其中 z 是列表中的位置,y 是插入到列表 xs 中的数字。我是haskell的新手,这就是我到目前为止所拥有的。

insertAt :: Int-> Int-> [Int]-> [Int]
insertAt z y xs
  | z==1 = y:xs

但我不知道从那里去哪里。

我有一个 elementAt 函数,其中

elementAt v xs
  | v==1 = head xs
  | otherwise = elementAt (v-1) (tail xs)

但我不确定我如何才能适应它,或者我什至需要。如果可能的话,我想避免追加。

4

3 回答 3

7

如果这不是家庭作业:let (ys,zs) = splitAt n xs in ys ++ [new_element] ++ zs

对于这篇文章的其余部分,我将假设您将这个问题作为家庭作业或自学如何做这种事情。

这类问题的关键在于将其分解为自然情况。您正在处理两条数据:您要插入的列表和该列表中的位置。在这种情况下,每条数据都有两种自然情况:您正在处理的列表可以为空或不为空,您正在处理的数字可以为零或不为零。所以第一步是写出所有四种情况:

insertAt 0 val []     = ...
insertAt 0 val (x:xs) = ...
insertAt n val []     = ...
insertAt n val (x:xs) = ...

现在,对于这四种情况中的每一种,您都需要考虑在这种情况下应该给出什么答案。

对于前两种情况,答案很简单:如果要插入到列表的前面,只需将您感兴趣的值放在开头即可,无论列表是否为空。

第三种情况表明问题中实际上存在歧义:如果要求您插入例如空列表的第三个位置会发生什么?对我来说听起来像是一个错误,但你必须自己回答在这种情况下你想做什么。

第四种情况最有趣:假设您想将一个值插入一个非空列表的非第一个位置。在这种情况下,请记住您可以使用递归来解决问题的较小实例。在这种情况下,您可以使用递归来解决,例如,insertAt (n-1) val xs将相同的值插入到输入列表尾部的n-1第 th 位置的结果。例如,如果您尝试将 5 插入到 list 的位置 3(第四个位置)[100,200,300],您可以使用递归将 5 插入到 list 的位置 2(第三个位置)[200,300],这意味着递归调用会产生[200,300,5].

我们可以假设递归调用会起作用;我们现在唯一的工作就是将那个小问题的答案转换成我们最初给出的问题的答案。例子中我们想要的答案是[100,200,300,5](将 5 插入到 list 的位置 4 的结果[100,200,300],而我们得到的就是 list [200,300,5]。那么如何才能得到我们想要的结果呢?只要在第一个元素上加回来!(想想为什么这是真的。)

完成该案例后,我们已经涵盖了要更新的列表和位置组合的所有可能案例。由于我们的函数将在所有可能性下正常工作,并且我们的可能性涵盖所有可能的输入,这意味着我们的函数将始终正常工作。所以我们完成了!

我将把这些想法转化为 Haskell 的工作留给你,因为练习的目的是让你学习它,但希望这能让你知道如何解决问题。

于 2013-10-04T00:14:48.177 回答
4

您可以在索引 z 处拆分列表,然后将列表的第一部分与元素(使用++ [y])连接,然后与列表的第二部分连接。但是,这将创建一个新列表,因为默认情况下数据是不可变的。按照惯例,列表的第一个元素具有索引0(因此z,如果您希望第一个元素的含义由 索引,请进行相应调整1)。

insertAt :: Int -> Int-> [Int] -> [Int] 
insertAt z y xs = as ++ (y:bs)
                  where (as,bs) = splitAt z xs
于 2013-10-03T23:05:51.287 回答
1

虽然上述答案是正确的,但我认为这更简洁:

insertAt :: Int -> Int-> [Int]-> [Int]
insertAt z y xs = (take z xs) ++ y:(drop z xs)
于 2020-12-08T21:14:54.533 回答