【问题标题】:Duplicate elements of a list on even index偶数索引上列表的重复元素
【发布时间】:2018-06-08 19:41:14
【问题描述】:

在 Haskell 中,我如何实现一个函数 dup 复制列表中偶数位置 (0,2,4...) 上的所有元素

dup :: [a] -> [a]
dup [] = []
dup (x:xs) =  x : x : dup xs
//but only on even index ??

调用示例:

dup [1,5,2,8,4] = [1,1,5,2,2,8,4,4]

【问题讨论】:

  • 通过为dup 编写函数定义。你有什么尝试吗?
  • @WillemVanOnsem 添加了我尝试过的内容
  • 使用模式匹配,以便一次处理两个元素:dup (x:y:xs) = x:x:y:dup xs。如果您认为x 有一个偶数索引,那么y 必须有一个奇数索引,剩下的就是其中包含零个或一个元素的列表的基本情况。

标签: haskell functional-programming


【解决方案1】:

我们可以定义两个执行相互递归的函数:dupeven :: [a] -> [a]dupodd :: [a] -> [a]dupeven 因此将复制第一个元素,并递归传递给dupodd。另一方面,dupodd 只复制头部,然后对dupeven 执行递归。喜欢:

dupeven :: [a] -> [a]
dupeven [] = []
dupeven (x:xs) = x : x : dupodd xs

dupodd :: [a] -> [a]
dupodd [] = []
dupodd (x:xs) = x : dupeven xs

好消息是我们得到了两个重复的变体。此外,这两个函数都相当简单:它们只对空列表[] 和“缺点”(:) 这两种不同模式进行操作。

因此,这按预期工作,此外,我们基本上以(相当)低实施成本有一个额外的功能:

Prelude> dupeven [1,5,2,8,4]
[1,1,5,2,2,8,4,4]
Prelude> dupodd [1,5,2,8,4]
[1,5,5,2,8,8,4]

【讨论】:

    【解决方案2】:

    正如其他答案所解释的,您可以从第一原理递归地编写此函数,但我始终认为此类问题是有趣的难题:您如何从现有的基础库中编写这样的函数?

    首先,当你想索引一个列表时,你可以zip它使用一个惰性求值的无限列表:

    Prelude> zip [0..] [1,5,2,8,4]
    [(0,1),(1,5),(2,2),(3,8),(4,4)]
    

    不过,在这种情况下,您并不需要知道实际的索引值(0, 1, 2, 3, 4 等)。相反,您只需要知道每个数字需要重复多少次。您可以通过无限循环21 来产生这些知识:

    Prelude> take 10 $ cycle [2,1]
    [2,1,2,1,2,1,2,1,2,1]
    

    (上面的示例使用take 10 来停止评估列表,否则将永远继续。)

    您可以zip (cycle [2,1]) 与任何输入列表一起获取元组列表:

    Prelude> zip (cycle [2,1]) [1,5,2,8,4]
    [(2,1),(1,5),(2,2),(1,8),(2,4)]
    

    元组的第一个元素是重复第二个元素的次数。

    您可以使用replicate 多次重复任何值,但您必须使用uncurry 将单个元组作为输入:

    Prelude> uncurry replicate (2,1)
    [1,1]
    Prelude> uncurry replicate (1,5)
    [5]
    

    请注意,此函数始终返回一个列表,因此如果您对元组列表执行此操作,您将获得一个列表列表。为了立即展平这样的列表,您可以使用 monadic bind 展平投影:

    Prelude> zip (cycle [2,1]) [1,5,2,8,4] >>= uncurry replicate
    [1,1,5,2,2,8,4,4]
    

    如果你愿意,你可以用它做一个函数:

    dup xs = zip (cycle [2,1]) xs >>= uncurry replicate
    

    这个函数被证明是参数多态的,所以虽然你可以将它用于整数列表,但你也可以将它用于字符列表:

    Prelude> dup [1,5,2,8,4]
    [1,1,5,2,2,8,4,4]
    Prelude> dup "acen"
    "aaceen"
    

    【讨论】:

    • 我真的很喜欢这个答案中采用的教学方法。只有一个小建议:考虑zipWith replicate,而不是生成并立即使用一个元组。然后,既然您已经完成了这项工作,那么在>>= 的另一边,您对id 无能为力。 (或者将foo >>= id 专门化为concat foo,这对于初学者和专家来说可能更易读。)因此我的最终形式是dup = concat . zipWith replicate (cycle [2,1])
    【解决方案3】:

    您可能想要创建一组相互递归的函数

    duplicate, duplicate' :: [a] -> [a]
    duplicate [] = []
    duplicate (x:xs) =  x : x : duplicate' xs
    duplicate' [] = []
    duplicate' (x:xs) = x : duplicate xs
    

    或者添加一个简单的ADT来确定下一步的动作

    data N = O | T
    duplicate = duplicate' T
    duplicate' _ [] = []
    duplicate' T (x:xs) = x : x : duplicate' O xs
    duplicate' O (x:xs) = x : duplicate' T xs
    

    但老实说,最好的方法是 @Simon_Shine 建议的,

    duplicate [] = []
    duplicate (x:y:xs) = x : x : y : duplicate xs
    duplicate (x:xs) = x : x : xs -- Here x is an even index and xs is an empty list
    

    【讨论】:

      猜你喜欢
      • 2016-06-26
      • 2018-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-01
      • 2019-12-31
      相关资源
      最近更新 更多