Haskell/Solutions/列表 III
外观
练习 |
---|
|
and [] = True
and (x:xs) = x && and xs
--As a fold
and = foldr (&&) True
or [] = False
or (x:xs) = x || or xs
--As a fold
or = foldr (||) False
maximum :: Ord a => [a] -> a -- omitting type declaration results in "Ambiguous type variable" error
maximum = foldr1 max --foldl1 also works, but not if you want infinite lists. Not that you can ever be sure of the maximum of an infinite list.
minimum :: Ord a => [a] -> a
minimum = foldr1 min --Same as above.
--Using foldl'; you will want to import Data.List in order to do the same.
reverse :: [a] -> [a]
reverse = foldl' flippedCons []
where
flippedCons xs x = x : xs
练习 |
---|
编写你自己的scanr 定义,首先使用递归,然后使用foldr 。对scanl 做同样的事情,首先使用递归,然后使用foldl 。 |
-- with recursion
scanr2 step zero [] = [zero]
scanr2 step zero (x:xs) = step x (head prev):prev
where prev = scanr2 step zero xs
scanl2 step zero [] = [zero]
scanl2 step zero (x:xs) = zero : scanl2 step (step zero x) xs
-- An alternative scanl with poorer performance. The problem is that
-- last and init, unlike head and tail, must run through the entire
-- list, and (++) does the same with its first argument.
scanl2Slow step zero [] = [zero]
scanl2Slow step zero xs = prev ++ [step (last prev) (last xs)]
where prev = scanl2Slow step zero (init xs)
--with fold
scanr3 step zero xs = foldr step' [zero] xs
where step' x xs = step x (head xs):xs
scanl3 step zero xs = (reverse . foldl step' [zero]) xs
where step' xs x = step (head xs) x:xs
-- This implementation has poor performance due to (++), as explained
-- for scanl2Slow. In general, using (++) to append to a list
-- repeatdely is not a good idea.
scanl3Slow step zero xs = foldl step' [zero] xs
where step' xs x = xs ++ [step (last xs) x]
在上面高效的scanr
实现中,我们使用head
和tail
来避免必须在 where 子句中分解列表参数并进行模式匹配,只是为了在右侧重新组装它。在模式匹配章节中,我们将看到 as 模式如何让我们在不需要head
的情况下实现同样的效果。
练习 |
---|
定义以下函数
|
factList n = scanl1 (*) [1..n]
factList' n = scanl (*) 1 [2..n]
练习 |
---|
编写一个 |
returnDivisible :: Int -> [Int] -> [Int]
returnDivisible n xs = [ x | x<-xs , (mod x n) == 0 ]
--or, if you prefer to use filter...
returnDivisible :: Int -> [Int] -> [Int]
returnDivisible n = filter (\x -> (mod x n) == 0)
练习 |
---|
|
choosingTails :: [[Int]] -> [[Int]]
choosingTails ls = [ tail l | l <- ls, not (null l), head l > 5 ]
列表推导中的布尔条件按顺序计算,因此如果你交换了条件,如下所示
choosingTails ls = [ tail l | l <- ls, head l > 5, not (null l) ]
如果其中一个l原来是一个空列表,程序将尝试对其应用 head,这会导致错误。首先放置not (null l)会导致程序在遇到空列表时,短路条件 - 也就是说,忽略第二个条件,因为第一个条件为假足以拒绝列表 - 因此避免执行 head [] 以及随之而来的错误。或者,你也可以通过模式匹配来过滤。只有非空列表才匹配(l:ll)
,它被第一个元素l
以及可能为空的尾部ll
划分
choosingTails ls = [ ll | (l:ll) <- ls, l > 5 ] -- filter by pattern match
练习 |
---|
在这部分,我们已经看到列表推导本质上是filter和map的语法糖。现在反向操作,并使用列表推导语法定义filter和map的替代版本。 |
alternativeFilter :: (a -> Bool) -> [a] -> [a]
alternativeFilter cond xs = [ x | x<-xs, cond x ]
alternativeMap :: (a -> b) -> [a] -> [b]
alternativeMap f xs = [ f x | x<-xs ]
--no need for any condition, as all x will be accepted
练习 |
---|
重写doubleOfFirstForEvenSeconds使用filter和map而不是列表推导。 |
doubleOfFirstForEvenSeconds' :: [(Int, Int)] -> [Int]
doubleOfFirstForEvenSeconds ps =
let f pair = 2 * (fst pair)
g pair = isEven (snd pair)
in map f (filter g ps)
--isEven, naturally, remains the same.
isEven :: Int -> Bool
isEven n = (mod n 2 == 0)
doubleOfFirstForEvenSeconds' :: [(Int, Int)] -> [Int]
doubleOfFirstForEvenSeconds' ps = map ((2*) . fst) (filter (isEven . snd) ps)