如何创建Haskell函数,返回整数列表中的每个第三个元素

时间:2018-02-01 02:33:38

标签: haskell functional-programming

我想创建一个函数,从int的列表返回每三个int,而不使用任何预定义的函数。例如,everyThird [1,2,3,4,5] --> [1,4]

everyThird:: [a] -> [a]

我可以继续使用tail迭代列表并每隔三次调用追加到一个新列表吗?我是Haskell的新手并且非常困惑所有这些

5 个答案:

答案 0 :(得分:9)

另一种方法是处理三种不同的基本情况,在所有这些情况下,我们位于列表的末尾,列表长度少于三个元素,一个递归情况,列表位于至少三个元素:

everyThird :: [a] -> [a]
everyThird []         = []
everyThird [x]        = [x]
everyThird [x, _]     = [x]
everyThird (x:_:_:xs) = x:everyThird xs

答案 1 :(得分:5)

您希望完全按照您的说法执行操作:遍历列表并仅在每次第三次调用时包含元素。但是,这是一个问题。 Haskell是一种有趣的语言,其中包含了#34;更改"一个变量没有意义,所以通常的方法有一个计数器变量i,它告诉我们我们是否依赖第三个元素"不会以通常的方式工作。相反,我们将创建一个递归辅助函数来维护我们的计数。

everyThird :: [Int] -> [Int]
everyThird xs = helper 0 xs
  where helper _ [] = []
        helper 0 (x : xs) = x : helper 2 xs
        helper n (_ : xs) = helper (n - 1) xs

我们在帮手中有三个案例。

  1. 如果列表为空,请停止并返回空列表。
  2. 如果计数器为0(即,如果我们在第三个元素上),请创建一个以当前元素开头并以其余计算结束的列表。
  3. 如果计数器不为零,则倒计时并继续迭代。
  4. 由于模式匹配的工作方式,它将按顺序尝试这三个语句。

    注意我们如何使用另一个参数作为计数器变量,因为我们不能像在命令式语言中那样改变变量。另外,请注意我们如何递归地构造列表;我们永远不会"追加"到现有的列表,因为这意味着我们正在改变列表。我们只是从头开始构建列表,并在第一轮结束时得到正确的结果。

答案 2 :(得分:5)

Haskell没有经典迭代(即没有循环),至少没有monad,但你可以使用类似的逻辑,就像你在for循环中使用索引[0..]压缩列表并应用适当的函数一样来自Data.List。

E.g。你需要做的是过滤每三个元素:

everyThirdWithIndexes list = filter (\x -> snd x `mod` 3 == 0) $ zip list [0..]

当然你必须摆脱索引,有两种优雅的方法可以做到这一点:

everyThird list = map (fst) . everyThirdWithIndexes list
-- or:
everyThird list = fst . unzip . everyThirdWithIndexes list

如果你不熟悉filter和map,你可以定义一个简单的递归,它从列表的每个第一个元素构建一个列表,删除接下来的两个,然后从一个新的函数调用中添加另一个:

everyThird [] = []  -- both in case if the list is empty and the end case
everyThird (x:xs) = x : everyThird (drop 2 xs)

编辑:如果您对这些解决方案有任何疑问(例如您不熟悉的某些语法),请随时在评论中提问。 :)

答案 3 :(得分:4)

一种经典的方法:

everyThird xs = [x | (1,x) <- zip (cycle [1..3]) xs]

答案 4 :(得分:2)

您还可以使用Data.List.Split中的chunksOf将列表分成3个块,然后只映射每个列表中的第一个元素:

import Data.List.Split

everyThird :: [a] -> [a]
everyThird xs = map head $ chunksOf 3 xs

其工作原理如下:

*Main> everyThird [1,2,3,4,5]
[1,4]

注意:您可能需要运行cabal install split才能使用chunksOf