我正在嘗試完成 Haskell 九十九題中的第 8 題,但是我在理解為什么我的函式的串列結果排序錯誤時遇到了問題。
該compress函式的目的是從輸入串列中消除任何重復的字母,并按照它們在輸入串列中首次出現的順序輸出包含唯一字母的另一個串列。這是我的壓縮功能代碼:
compress l = foldr f [] l where f a b = if a `elem` b then b else a : b
當重復字母彼此相鄰時,它可以正常作業,因此“aaaabbb”輸出“ab”,這是正確的,但是當重復字母被另一個字母分隔時,它會改變其在輸出中的順序,因此“aba”輸出“ba”,而預期輸出是“ab”。
即使在為 foldr 寫出堆疊跟蹤時,我似乎也得到了預期的輸出,但是當在 GHCI 中使用諸如“aba”或“abca”之類的輸入運行代碼時,我得到了不正確的結果。是什么導致了這種行為?為什么當重復的字母被不同的字母分隔時,輸出的順序會改變?
uj5u.com熱心網友回復:
該foldr函式以一個初始值(型別b)和一個Foldable容器開始t。對于容器中的“每個”值,它使用該值和“當前”值a呼叫函式。(a -> b -> b)ab
Prelude> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
在compress中,初始值為[],這也使編譯器能夠推斷出Foldable實體為[]。
現在嘗試 GHCi 中的步驟。將(僅用于 GHCi 會話)定義f為頂級函式:
Prelude> f a b = if a `elem` b then b else a : b
如果輸入為"aba",則第一次f呼叫時,b值將是[],a值將是'a',因為foldr從右側折疊。
Prelude> f 'a' []
"a"
回傳值"a"現在成為下一次的累加器值b:
Prelude> f 'b' "a"
"ba"
這是因為fconses'b'到"a".
累加器值現在是"ba"。f使用串列中的第三個也是最后一個值再次將其傳遞給:
Prelude> f 'a' "ba"
"ba"
參見例如this other answer,它概述了探索和“除錯”Haskell函式的互動式方式。
uj5u.com熱心網友回復:
compress l = foldr f [] l where f a b = if a `elem` b then b else a : b...
"aba"輸出"ba",而預期輸出是"ab".
foldr上串列很簡單。它被定義為
foldr g z [a,b,c,...,n] = g a (foldr g z [b,c,...,n])
-- and by generalization,
foldr g z [ n] = g n z
-- which means
foldr g z [ ] = z
這就是它的全部。看起來不言自明,不是嗎?只需從語法上重新撰寫您的foldr呼叫,即可立即準確了解發生了什么。特別是,稍微重寫你的定義,我們有
compress xs = foldr f [] xs
where
f a r = if elem a r then r
else a : r
(我確實(嘗試)總是呼叫組合函式“ r”的第二個引數,用于“輸入串列其余部分的遞回計算結果”。)
因此我們有
compress [a,b,c,...,n]
= foldr f [] [a,b,c,...,n]
= f a ( foldr f [] [b,c,...,n] )
= if elem a r then r
else a : r
where
r = foldr f [] [b,c,...,n]
= if elem a r then r
else a : r
where
r = compress [b,c,...,n]
(where就好像它是運算式的一部分一樣使用,例如“翻轉let”)。換一種說法,
compress (x:xs) = if elem x r
then r
else x : r
where r = compress xs
compress [] = []
This reads: "if x is present in the compressed rest of input, skip it; else, keep it; and continue with the compressed rest". (so, calling it b is misleading; suggests a and b are similar entities; they are not -- a (or x) is an element of the list, and r is its transformed tail).
So you see where the problem is: if there are more then one as in the list, this definition keeps the last one, whereas you want to keep the first.
So if there are several of them in a row this difference is unobservable:
-- 1. -- 2.
a a a a b b b a a a a b b b
a b a b
but if there's an intervening character then of course we can see the difference:
-- 1. -- 2.
a a a a b b b a a a a a b b b a
b a a b
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/427290.html
