2012-09-22 33 views
0

所以我被要求在Haskell中創建一個二叉樹作爲輸入整數列表。以下是我的代碼。我的問題是列表的最後一個元素沒有插入樹中。例如[1,2,3,4]它只能插入到樹中,直到樹中沒有插入「3」和4。最後一個元素沒有在樹中插入

data ArbolBinario a = Node a (ArbolBinario a) (ArbolBinario a) | EmptyNode 
deriving(Show) 

    insert(x) EmptyNode= insert(tail x) (Node (head x) EmptyNode EmptyNode) 

    insert(x) (Node e izq der) 
    |x == [] = EmptyNode --I added this line to fix the Prelude.Head Empty List error, after I added this line the last element started to be ignored and not inserted in the tree 
    |head x == e = (Node e izq der) 
    |head x < e = (Node e (insert x izq) der) 
    |head x > e = (Node e izq (insert x der)) 

關於這裏發生了什麼的任何想法?幫助深表感謝

回答

2

爲了解決這個問題,我已經使用其他功能

data Tree a = Node a (Tree a) (Tree a) | EmptyNode deriving(Show) 

insert :: Ord a => a -> Tree a -> Tree a 
insert x EmptyNode = Node x EmptyNode EmptyNode 
insert x (Node y left right) 
    | x == y = (Node y left right) 
    | x < y = (Node y (insert x left) right) 
    | x > y = (Node y left (insert x right)) 

buildtree :: Ord a => [a] -> Tree a 
buildtree []  = EmptyNode 
buildtree (x:xs) = insert x (buildtree xs) 

tree :: Ord a => [a] -> Tree a 
tree xs = buildtree (reverse xs) 

,構建一個二叉樹,你需要調用buildtree功能。問題是你需要先倒轉列表。所以,tree將完成這項工作。

*Main> insert 5 (insert 12 (insert 2 (insert 10 EmptyNode))) 
Node 10 (Node 2 EmptyNode (Node 5 EmptyNode EmptyNode)) (Node 12 EmptyNode EmptyNode) 

*Main> tree [10,2,12,5] 
Node 10 (Node 2 EmptyNode (Node 5 EmptyNode EmptyNode)) (Node 12 EmptyNode EmptyNode) 

*Main> buildtree [5,12,2,10] 
Node 10 (Node 2 EmptyNode (Node 5 EmptyNode EmptyNode)) (Node 12 EmptyNode EmptyNode) 

UPDATE

你能避免使用其他功能,這樣一來:

insert2 :: Ord a => [a] -> Tree a -> Tree a 
insert2 []  t = t 
insert2 (x:xs) EmptyNode = insert2 xs (Node x EmptyNode EmptyNode) 
insert2 (x:xs) (Node y left right) 
      | x == y = insert2 xs (Node y left right) 
      | x < y = insert2 xs (Node y (insert2 [x] left) right) 
      | x > y = insert2 xs (Node y left (insert2 [x] right)) 

然而,它並不像使用與foldl或其他方式,唯一的好東西是有效的關於它只使用一個函數來完成所有事情,不需要反向。

*Main> insert2 [10,2,12,5] EmptyNode 
Node 10 (Node 2 EmptyNode (Node 5 EmptyNode EmptyNode)) (Node 12 EmptyNode EmptyNode) 
+1

「buildTree」的實現是摺疊而不是遞歸的最佳選擇。這不僅更簡單,而且不需要列表反轉。 –

+0

是的,你是對的。謝謝你指出這個 – user1406062

+0

看來我錯過了這種情況:insert2 [] t = t並在創建節點之前調用我的funcion發送尾部。非常感謝您的詳細解釋。 Haskell是一個棘手但很酷的編程語言:) – rdk1992

4

這裏沒有什麼幫助是你混合的擔憂。爲什麼沒有一個將單個元素插入到樹中的函數,而是另一個將列表中的所有元素添加到樹中的函數?例如。

data ArbolBinario a = Node a (ArbolBinario a) (ArbolBinario a) 
        | EmptyNode 
        deriving(Show) 

-- insert handles only one element 
insert :: (Ord a) => a -> ArbolBinario a -> ArbolBinario a 
insert x EmptyNode = Node x EmptyNode EmptyNode 
insert x [email protected](Node e izq der) 
    | x == e = n 
    | x < e = (Node e (insert x izq) der) 
    | x > e = (Node e izq (insert x der)) 

-- insertList folds the list into a tree 
insertList :: (Ord a) => [a] -> ArbolBinario a -> ArbolBinario a 
insertList xs t = foldl (\a x -> insert x a) t xs 

使用諸如foldl之類的東西,您不必擔心在到達列表末尾時會發生什麼。結果是:

insertList [5, 3, 7, 9, 1, 4, 2, 6] EmptyNode 
-- output: 
-- Node 5 (Node 3 (Node 1 EmptyNode (Node 2 EmptyNode EmptyNode)) (Node 4 EmptyNode EmptyNode)) (Node 7 (Node 6 EmptyNode EmptyNode) (Node 9 EmptyNode EmptyNode)) 

希望有所幫助。

編輯----

我還想指出的是,它可能是一個更好的主意遵循什麼其他的Haskell函數做和改變你的參數的順序。看到這一點:

data ArbolBinario a = Node a (ArbolBinario a) (ArbolBinario a) 
        | EmptyNode 
        deriving(Show) 

insert :: (Ord a) => ArbolBinario a -> a -> ArbolBinario a 
insert EmptyNode x = Node x EmptyNode EmptyNode 
insert [email protected](Node e izq der) x 
    | x == e = n 
    | x < e = (Node e (insert izq x) der) 
    | x > e = (Node e izq (insert der x)) 

insertList :: (Ord a) => ArbolBinario a -> [a] -> ArbolBinario a 
insertList = foldl insert 

這意味着你的功能,可與秋後算賬(如褶皺,掃描等)更好地利用。你可以看到如何簡化insertList的定義。

僅供參考:)

+2

我不同意您的編輯:爲了與其他Haskell函數保持一致,最好使二叉樹成爲最後一個參數。看看Data.Map中的[insert/delete/update函數](http://www.haskell.org/ghc/docs/latest/html/libraries/containers/Data-Map-Lazy.html#v:insert)例如:他們都將輸入地圖作爲最終參數。這使得它們易於組成,例如'newMap =插入「foo」4。刪除「欄」。調整(+4)「baz」$ oldMap'。 – dave4420

+2

@ dave4420 - 你確實是對的。這似乎是合乎邏輯的事情,但現在它變得不那麼有意義。感謝指針。 –

3

你已經得到了寫insert功能的更清潔的方式提出了一些建議,但至今沒有人告訴你什麼地方錯了你實現,所以讓我開始與:

data ArbolBinario a = Node a (ArbolBinario a) (ArbolBinario a) | EmptyNode 
         deriving(Show) 

insert(x) EmptyNode= insert(tail x) (Node (head x) EmptyNode EmptyNode) 

insert(x) (Node e izq der) 
|x == [] = EmptyNode 
|head x == e = (Node e izq der) 
|head x < e = (Node e (insert x izq) der) 
|head x > e = (Node e izq (insert x der)) 

爲簡潔起見,我使用了一個較短的列表並將其縮寫爲EmptyNodeE

insert [1,2] E 
~> insert [2] (Node 1 E E) 
-- 2 > 1, so the last clause, head x > e, is used 
~> Node 1 E (insert [2] E) 
-- insertion in empty tree, first equation is used 
~> Node 1 E (insert [] (Node 2 E E)) 
-- Now the first clause of the second equation is used 
~> Node 1 E (E) 

當所有元素都被插入並且您到達列表的末尾時,您將刪除該節點而不是執行任何操作。最小的改變來解決這個將改變爲insert第二個方程的第一款以

|x == [] = Node e izq der 

這將然而,仍然留下一個失敗案例(你已經有了),

insert [] EmptyNode = insert (tail []) (Node (head []) EmptyNode EmptyNode) 

會引起*** Exception: Prelude.tail: empty list

除了成爲上述錯誤的原因之外,這裏使用的headtail也是高度單一的。定義這種功能的常用方法是在列表上進行模式匹配。另外單向性是你檢查一個空的列表,x == [],慣用的方法是使用null x。在這種情況下,其他守衛要求元素類型爲Ord的實例,因此沒有語義變化,但通常,x == []對元素類型施加了Eq約束,而null x對任意類型起作用。

最後,儘管你認爲你的警衛head x == ehead x < ehead x > e包括所有的可能性(爲有效Ord情況下,他們這樣做 - 除外浮點類型,其中NaN既不等於也不高於比任何值小也不大,但是否這些Ord實例是有效的是辯論的問題),編譯器不能確定這一點,並且(當被要求警告通常應該是這樣的事情時,總是編譯-Wall)警告關於非窮舉模式定義爲insert。要以編譯器知道所有案例都涵蓋的方式覆蓋所有案例,最後一個警衛應具有otherwise條件。

使您的代碼放到一個更地道的形狀(和固定的優秀insert [] EmptyNode錯誤)在

insert :: Ord a => [a] -> ArbolBinario a -> ArbolBinario a 
insert []  t   = t  -- if there's nothing to insert, don't change anything 
insert (x:xs) EmptyNode = insert xs (Node x EmptyNode EmptyNode) 
-- Using as-patterns, `l` is the entire list, `x` its head, `t` the entire tree 
insert [email protected](x:_) [email protected](Node e izq der) 
    | x == e    = t 
    | x < e    = Node e (insert l izq) der 
    | otherwise   = Node e izq (insert l der) 

結果現在我們可以尋找進一步的問題。 insert的一個可能意想不到的方面是,如果要插入的元素列表的頭部已經在樹中,則整個列表被丟棄並且樹完全不變,例如,

insert [1 .. 10] (Node 1 EmptyNode EmptyNode) = Node 1 EmptyNode EmptyNode 

處理這種事情的通常方法是隻刪除列表頭並仍插入其餘元素。這將改變該定義的最後一個方程來實現

insert [email protected](x:xs) [email protected](Node e izq der) 
    | x == e    = insert xs t 
    | x < e    = Node e (insert l izq) der 
    | otherwise   = Node e izq (insert l der) 

更可能的意外方面

  • insert xs EmptyNode總是產生一個樹,其中每個節點只有一個(或無,爲最低)非空子樹,即構建的樹基本上是一個列表。
  • 定義的最後等式中的子句強烈地表明樹應該是二叉搜索樹,但該屬性不被定義維護。例如

    insert [1,10] (Node 3 (Node 2 E E) (Node 7 E E)) 
    ~> Node 3 (insert [1,10] (Node 2 E E)) (Node 7 E E) 
    ~> Node 3 (Node 2 (insert [1,10] E) E) (Node 7 E E) 
    ~> Node 3 (Node 2 (insert [10] (Node 1 E E)) E) (Node 7 E E) 
    ~> Node 3 (Node 2 (Node 1 E (Node 10 E E)) E) (Node 7 E E) 
    
             3 
            /\ 
            / \ 
            2  7 
           /
           /
           1 
            \ 
            \ 
            10 
    

解決這些問題,如OJ. suggested before,以分離出插入單個元件成樹

insertOne :: Ord a => a -> ArbolBinario a -> ArbolBinario a 
insertOne x EmptyNode = Node x EmptyNode EmptyNode 
insertOne x [email protected](Node e izq der) 
    | x == e = t 
    | x < e  = Node e (insertOne x izq) der 
    | otherwise = Node e izq (insertOne x der) 

的情況下,並使用該插入來自各個元件的最佳方式該列表,從頂部找到它的位置:

insertList :: Ord a => [a] -> ArbolBinario a -> ArbolBinario a 
insertList []  t = t 
insertList (x:xs) t = insertList xs (insertOne x t) 
-- Alternative way: 
-- insertList (x:xs) t = insertOne x (insertList xs t) 

這些計算模式非常常見,他們已經在Prelude定義的函數被抓獲:

insertList xs t = foldl (flip insertOne) t xs 
-- or, for the alternative way: 
-- insertList xs t = foldr insertOne t xs 

正如你所看到的,與insertOne自然參數順序,對摺了,我們需要應用flip組合子交換它的參數順序,其暗示列表的自然摺疊操作是正確的摺疊,foldr

但是,由於insertOne在它可以做任何事情之前需要知道它的樹參數,它不是一個專門用於右對摺的函數,在左對齊中使用它可以更有效(但實際上有一個效率增益,則必須使用嚴格的左側摺疊foldl',可從Data.List獲得,以及更嚴格的版本insertOne)。

+0

尼爾的解釋丹尼爾。我需要停止這麼懶惰,並開始正確回答問題。 –

相關問題