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

Pus*_*yon 0 haskell functional-programming

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

everyThird:: [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

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

Cha*_*Rex 9

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

everyThird :: [a] -> [a]
everyThird []         = []
everyThird [x]        = [x]
everyThird [x, _]     = [x]
everyThird (x:_:_:xs) = x:everyThird xs
Run Code Online (Sandbox Code Playgroud)


Sil*_*olo 5

您希望完全按照您的说法执行操作:遍历列表并仅在每次第三次调用时包含元素.但是,有一个问题.Haskell是一种有趣的语言,其中"改变"变量的想法没有意义,所以通常的方法"有一个反向变量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
Run Code Online (Sandbox Code Playgroud)

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

  1. 如果列表为空,请停止并返回空列表.
  2. 如果计数器为0(即,如果我们在第三个元素上),则创建一个以当前元素开头并以其余计算结束的列表.
  3. 如果计数器不为零,则倒计时并继续迭代.

由于模式匹配的工作方式,它将按顺序尝试这三个语句.

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


5ar*_*5ar 5

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

例如,您需要做的是过滤每第三个元素:

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

 

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

everyThird list = map (fst) . everyThirdWithIndexes list
-- or:
everyThird list = fst . unzip . everyThirdWithIndexes list
Run Code Online (Sandbox Code Playgroud)

 

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

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

 

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


dfe*_*uer 5

一种经典方法:

everyThird xs = [x | (1,x) <- zip (cycle [1..3]) xs]
Run Code Online (Sandbox Code Playgroud)