我想创建一个函数,从int的列表返回每三个int,而不使用任何预定义的函数。例如,everyThird [1,2,3,4,5] --> [1,4]
everyThird:: [a] -> [a]
我可以继续使用tail迭代列表并每隔三次调用追加到一个新列表吗?我是Haskell的新手并且非常困惑所有这些
答案 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
我们在帮手中有三个案例。
由于模式匹配的工作方式,它将按顺序尝试这三个语句。
注意我们如何使用另一个参数作为计数器变量,因为我们不能像在命令式语言中那样改变变量。另外,请注意我们如何递归地构造列表;我们永远不会"追加"到现有的列表,因为这意味着我们正在改变列表。我们只是从头开始构建列表,并在第一轮结束时得到正确的结果。
答案 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
。