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)