2010-05-15 58 views
9

Nói rằng tôi có những điều sau Haskell loại cây, trong đó "Nhà nước" là một wrapper đơn giản:Làm thế nào để chức năng tạo ra một bề rộng cây đầu tiên. (Với Haskell)

data Tree a = Branch (State a) [Tree a] 
      | Leaf (State a) 
      deriving (Eq, Show) 

Tôi cũng có một chức năng "mở rộng :: Tree a -> Tree một" mà phải mất một nút lá, và mở rộng nó thành một chi nhánh, hoặc lấy một chi nhánh và trả về nó không thay đổi gì. Loại cây này đại diện cho cây tìm kiếm N-ary. Tìm kiếm chiều sâu đầu tiên là một sự lãng phí, vì không gian tìm kiếm rõ ràng là vô hạn, vì tôi có thể dễ dàng tiếp tục mở rộng không gian tìm kiếm bằng cách sử dụng mở rộng trên tất cả các nút lá của cây và cơ hội vô tình bị thiếu mục tiêu-nhà nước là rất lớn ... do đó giải pháp duy nhất là một tìm kiếm rộng đầu tiên, thực hiện khá phong nha hơn here, mà sẽ tìm thấy giải pháp nếu nó ở đó.

Điều tôi muốn để tạo, mặc dù, cây đi qua tối đa tìm giải pháp. Đây là một vấn đề bởi vì tôi chỉ biết làm thế nào để làm điều này sâu đầu tiên, có thể được thực hiện bằng cách đơn giản gọi là "mở rộng" chức năng một lần nữa và một lần nữa vào nút con đầu tiên ... cho đến khi một mục tiêu nhà nước được tìm thấy. (Điều này thực sự sẽ không tạo ra bất kỳ điều gì khác sau đó là một danh sách thực sự khó chịu.)

Có thể cho tôi bất kỳ gợi ý nào về cách thực hiện điều này (hoặc toàn bộ thuật toán) hay không. ? (Hoặc bất kỳ nguồn nào về điều này, bởi vì tôi thấy khá ít.)

+0

Ngoài ra, bạn có thể muốn sử dụng thứ gì đó ngoài 'State' ở đó, vì tên đó được sử dụng trong thư viện chuẩn cho đơn vị nhà nước, có trách nhiệm gây nhầm lẫn cho mọi người. –

+0

Tôi nhận ra rằng ngay bây giờ khi tôi đang sử dụng các đơn vị nhà nước để thực hiện thuật toán của tôi, dựa trên những lời khuyên đưa ra ở đây. – wen

Trả lời

9

Bạn đã xem sốcủa Chris Okasaki chưa? Mô-đun Data.Tree bao gồm trình tạo cây monadic có tên là unfoldTreeM_BF sử dụng thuật toán được điều chỉnh từ giấy đó.

Dưới đây là một ví dụ mà tôi nghĩ rằng tương ứng với những gì bạn đang thực hiện:

Giả sử tôi muốn tìm kiếm một cây nhị phân vô hạn của chuỗi nơi tất cả các trẻ em trái là chuỗi mẹ cộng với "a", và bên phải trẻ em là cha mẹ cộng với "bb".Tôi có thể sử dụng để tìm kiếm unfoldTreeM_BF cây chiều rộng đầu tiên và trả lại cây tìm kiếm lên đến giải pháp:

import Control.Monad.State 
import Data.Tree 

children :: String -> [String] 
children x = [x ++ "a", x ++ "bb"] 

expand query x = do 
    found <- get 
    if found 
    then return (x, []) 
    else do 
     let (before, after) = break (==query) $ children x 
     if null after 
     then return (x, before) 
     else do 
      put True 
      return (x, before ++ [head after]) 

searchBF query = (evalState $ unfoldTreeM_BF (expand query) []) False 

printSearchBF = drawTree . searchBF 

Đây không phải là khủng khiếp khá, nhưng nó hoạt động. Nếu tôi tìm kiếm "aabb", tôi nhận được chính xác những gì tôi muốn:

| 
+- a 
| | 
| +- aa 
| | | 
| | +- aaa 
| | | 
| | `- aabb 
| | 
| `- abb 
| 
`- bb 
    | 
    +- bba 
    | 
    `- bbbb 

Nếu đây là loại điều bạn mô tả, không khó để thích ứng với loại cây của bạn.

UPDATE: Dưới đây là một phiên bản do-miễn expand, trong trường hợp bạn đang vào loại điều:.

expand q x = liftM ((,) x) $ get >>= expandChildren 
    where 
    checkChildren (before, []) = return before 
    checkChildren (before, t:_) = put True >> return (before ++ [t]) 

    expandChildren True = return [] 
    expandChildren _  = checkChildren $ break (==q) $ children x 

(Nhờ camccann cho thúc giục tôi ra khỏi thói quen cấu trúc điều khiển cũ Tôi hy vọng điều này Phiên bản có thể chấp nhận được.)

+1

Chúa ơi, mã đó, nó ... tôi ... nhưng ... bạn đang làm gì * với nhà nước nghèo khổ đó? Tên quái vật! –

+0

Vâng, nó xấu xí, nhưng nó đã được tắt đầu của tôi. Bạn sẽ làm điều này như thế nào? Tôi sẽ thừa nhận rằng tôi là một người mới bắt đầu, nhưng tôi không thấy một cách để nói với unfoldTreeM_BF để ngừng mở rộng trẻ em mà không có nhà nước. –

+0

Được rồi, tôi đã sửa lại điều ngớ ngẩn mà tôi đã làm với truy vấn trong tiểu bang, nhưng tôi vẫn nghĩ rằng tôi cần nó để biết khi nào nên dừng lại. Chẳng phải đây chính xác là lý do tại sao người xây dựng cây là monadic ngay từ đầu? –

5

Tôi tò mò tại sao bạn cần chức năng expand - tại sao không chỉ đơn giản là xây dựng toàn bộ cây một cách đệ quy và thực hiện bất kỳ tìm kiếm nào bạn muốn?

Nếu bạn đang sử dụng expand để theo dõi các nút nào được tìm kiếm, hãy xây dựng danh sách khi bạn đi có vẻ đơn giản hơn hoặc thậm chí là cấu trúc cây thứ hai.

Dưới đây là một ví dụ nhanh chóng mà chỉ trả về kết quả đầu tiên mà nó tìm thấy, với sự giả mạo Leaf constructor loại bỏ:

data State a = State { getState :: a } deriving (Eq, Show) 

data Tree a = Branch { 
    state :: State a, 
    children :: [Tree a] 
    } deriving (Eq, Show) 

breadth ts = map (getState . state) ts ++ breadth (concatMap children ts) 
search f t = head $ filter f (breadth [t]) 

mkTree n = Branch (State n) (map mkTree [n, 2*n .. n*n]) 

testTree = mkTree 2 

Đang cố gắng nó ra trong GHCi:

> search (== 24) testTree 
24 

Đối Ngược lại, đây là một ngây thơ tìm kiếm theo chiều sâu đầu tiên:

depth (Branch (State x) ts) = x : (concatMap depth ts) 
dSearch f t = head $ filter f (depth t) 

... tất nhiên là không thành công s để chấm dứt khi tìm kiếm với (== 24), bởi vì các nhánh bên trái nhất là chuỗi vô tận 2 giây.

+4

Chỉ cần đánh vần nó ra: "Bí quyết" để thực hiện tìm kiếm theo chiều rộng-chức năng đầu tiên là lặp qua danh sách cây, không phải là một cây duy nhất. Có thể hữu ích khi thêm chú thích kiểu vào mức độ bề rộng và tìm kiếm ở cấp cao nhất ở đây. – MtnViewMark

+0

Tôi hiểu cách giải pháp định hướng mức này hoạt động như thế nào cho quá trình truyền tải của BF, nhưng những gì Dennetik muốn là cây được kiểm tra đến mức mà giải pháp được tìm thấy. Điều này dường như tương đương với việc đánh số BF và tôi hiểu rằng các phương pháp định hướng cấp không dễ dàng mở rộng để đánh số, trong khi phương pháp dựa trên hàng đợi của Okasaki là. Đó là lý do tại sao tôi sử dụng 'unfoldTreeM_BF' trong câu trả lời của tôi. Tôi có thiếu gì đó không? Bạn có thể mở rộng cách khôi phục cây đã được kiểm tra với cách tiếp cận của bạn không? –

+0

Tôi đã sử dụng chức năng "mở rộng" vì tôi đã làm việc với Java quá lâu rồi, và quên rằng tôi có thể dựa vào đánh giá lười biếng để làm việc trên những cây vô hạn. Cảm ơn bạn đã nhắc tôi - vì đây là những gì tôi thu thập mã của bạn? (Hoặc tôi là ngớ ngẩn.) – wen

Các vấn đề liên quan