簡體   English   中英

如何編寫a-> b - > b - > b類型的函數來折疊樹

[英]How to write a function of type a-> b -> b -> b for folding a tree

一些背景:我在Haskell中有以下類型的foldT函數(如foldr但對於樹)。

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b 

此foldT僅將類型(a - > b - > b - > b)作為輸入函數。

我試圖找到一種方法將我的樹轉換為一個列表,並無法找到一種方法使我的追加函數采取形式(a - > b - > b - > b)。

以下是無效的,因為它不是正確的類型:

append x y z = append x:y:z 

任何幫助,將不勝感激。

謝謝!

由於你沒有發布它,我會假設你的樹是......

data Tree a = Leaf | Node a (Tree a) (Tree a)

...而且, foldTa -> b -> b -> b參數以與聲明它們相同的順序獲取Node構造函數的字段。

我試圖找到一種方法將我的樹轉換為列表

讓我們按照以下類型開始解決這個問題:

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b

您希望將其展平為列表,因此函數的結果類型必須為[a]

treeToList :: Tree a -> [a]

這讓我們知道如何繼續:

treeToList t = foldT f z t

附:

f :: a -> [a] -> [a] -> [a]
z :: [a]
t :: Tree a

現在,繼續論證。 z將代替無價值的Leaf s插入,因此它必須是[] 至於f ,它必須將從左右分支創建的子列表與Node的值直接組合在一起。 假設有序遍歷,我們有:

 treeToList t = foldT (\x l r -> l ++ x : r) [] t

或者,沒有提到t

 treeToList = foldT (\x l r -> l ++ x : r) []

就是這樣。 需要注意的是,重復使用左嵌套(++) (在這種情況下會發生,因為foldT遞歸地向下foldT分支)可能會非常低效。 在您關心性能的情況下,值得考慮實現連接函數的替代方法,例如差異列表

PS:關於術語的說明。 說一個函數“像折疊器但是對於樹”是不明確的,因為有兩種眾所周知的類似於foldr的函數。 首先,你有Foldable類的方法(參見Benjamin Hodgson的答案 ),無論你做什么,它都會將樹折成一個列表。 然后是更強大的catamorphisms ,例如你正在使用的foldT ,它們能夠利用樹結構。

哎呀,你為什么要寫代碼 GHC可以自動填寫Foldable的實例,其中包含您正在尋找的toList

{-# LANGUAGE DeriveFoldable #-}
import Data.Foldable

data Tree a = Leaf | Node a (Tree a) (Tree a) deriving Foldable

treeToList :: Tree a -> [a]
treeToList = toList

由於Node構造函數的字段順序treeToList這個定義將執行預順序遍歷

如果要將樹轉換為列表,那么函數相對容易,所以基本上如果你有樹結構,例如:

data Tree a = Leaf | Node (Tree a) a (Tree a) deriving (Eq, Ord, Show)

然后你必須從樹中提取值並將第一個值添加到空列表中,然后在list(++)運算符的幫助下遞歸地將相同的方法應用於樹的其他分支以附加所有結果到一個列表,即:

toList :: Tree a -> [a]
toList Leaf                    =    []
toList (Node left value right) =    [value] ++ toList left ++ toList right

然后折疊它,首先:

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b

函數簽名缺少一個參數這里讓我們看一下簽名: a(第一個參數) - > b(第二個) - > b(第三個) - > b(返回類型)函數接受3個參數 /參數但你提供的簽名 只有1 b類型 (我猜你的累加器),所以基本上這將是你正在尋找的函數簽名:

foldT :: (a -> b -> b) -> b -> Tree a -> b

現在我們可以使用這個簽名,然后讓我們進入函數:

foldT :: (a -> b -> b) -> b -> Tree a -> b
foldT f acc Leaf                    =   acc
foldT f acc (Node left value right) =   foldT f (foldT f (f value acc) left) right

雖然我不認為我應該給你答案,你應該用遞歸數據結構練習更多,這樣你就可以更好地理解如何遍歷它們。

PS:如果你打算投票給我,那么至少要解釋一下原因!

你每次開始的第一件事就是開始匹配模式:)那時,它幾乎是機械的!

假設你有:

data Tree a = Leaf | Node (Tree a) a (Tree a)

讓我們開始匹配模式吧! :)

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z ...

什么是第一個Tree構造函數? 這是Leaf

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z Leaf = ...

我們可以放在那里? 我們需要類型為b東西......我們只有一種方法可以獲得它。 通過使用z :)

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z Leaf = z

因此,從機械上講,我們可以繼續下一個可能的模式:

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z Leaf           = z
foldT f z (Node t1 x t2) =

好吧,我們可以把它做成z ,但那有點無聊。 我們可能想要使用t1xt2 我們可以使用f ,它需要類型a東西......我們有x :: a :)

foldT :: (a -> b -> b -> b) -> b -> Tree a -> b
foldT f z Leaf           = z
foldT f z (Node t1 x t2) = f x ??? ???

f還有兩個b類型的參數,那么你認為我們可以把它放在那里? 我們可以把z放兩次,但那有點無聊。 我們怎么能從t1t2得到b

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM