有没有办法在 Haskell 中使用“<=”进行模式匹配?

问题描述

我有以下代码,它删除列表中的每个第 n 个元素。

dropEvery :: [a] -> Int -> [a]
dropEvery xs n = f xs n ++ dropEvery (drop n xs) n
    where
        f ys 0 = []
        f ys 1 = []
        f [] m = []
        f (y:ys) n = y : (f ys (n-1))

我想让它更短一点,并且想知道是否有一种方法可以在模式匹配中使用“where 子句执行此操作,但不起作用,为什么?

f ys m = []
     where
         m <= 1 || ys == []

我怎样才能避免这种冗余?在模式匹配中使用“小于或等于”有什么好方法吗?

编辑:我用守卫试过这个

 where
     f ys m
        | m <= 1 || null ys = []
        | otherwise         = (head ys) : (f (tail ys) (n-1))

解决方法

您可以与守卫一起工作:

dropEvery :: [a] -> Int -> [a]
dropEvery xs n = f xs n ++ dropEvery (drop n xs) n
    where
        f ys i | i <= 1 = []
        f [] _ = []
        f (y:ys) n = y : (f ys (n-1))

如果满足守卫中的条件,则该子句“触发”,因此在这种情况下将返回一个空列表[]

然而你会陷入无限循环,因为你写了 f xs n ++ dropEvery (n xs) ndrop 3 [] 会返回 [],因此它会继续调用 dropEvery 并带有一个空列表.

您可以使用递归,每次递减 n 直到达到 0,然后我们进行两次跳跃,因此:

dropEvery :: Int -> [a] -> [a]
dropEvery n = go (n-1)
    where go _ [] = []
          go i (x:xs)
              | i <= 0 = go (n-1) xs
              | otherwise = x : go (i-1) xs

我们还可以使用 splitAt :: [a] -> ([a],[a])模式保护

dropEvery n [] = []
dropEvery n ds
    | (_:ys) <- sb = sa ++ dropEvery n ys
    | otherwise = sa
    where (sa,sb) = splitAt (n-1) ds