2013-04-16 110 views
4

我知道遞歸函數是F#中的一項強大技術。我的問題是:是否有退出語句,可以跳出遞歸函數,就像命令式語言一樣。例如,將一個節點插入二叉樹。F#是否有循環退出語句?

type Tree<'a> when 'a :> IComparable<'a> = 
      | Nil 
      | Leaf of 'a 
      | Node of Tree<'a> * 'a * Tree<'a> 

let tt2 = Node(
       Node(Leaf "D", "B",Node(Leaf "G", "E", Leaf "H")), 
       "A", 
       Node(Nil, "C", Node(Nil, "F", Leaf "I"))) 

let rec contains (x : #IComparable<'a>) = function 
    | Nil -> false 
    | Leaf y -> if x.CompareTo(y) = 0 then true else false 
    | Node(l, y, r) -> 
     match l, y, r with 
      | l, y, Nil -> if x.CompareTo(y) = 0 then true else contains x l 
      | Nil,y, r -> if x.CompareTo(y) = 0 then true else contains x r 
      | _ -> if x.CompareTo(y) = 0 then true 
        else contains x r |>ignore 
         contains x l 

let xx = contains "C" tt2 //It is wrong answer. 

回答

10

是否有一個退出語句,它可以跳出遞歸函數,就像命令式語言。

不是。原因在於您可以通過遞歸函數和模式匹配來編碼命令性中斷/返回。如果你想中斷,只需返回一個值,否則調用另一個遞歸調用。

這個問題更適合要求高階函數。當您需要提前退出高階函數時,編寫自定義遞歸函數是一種方法。如果您對F#中的命令式結構感興趣,請查看@Tomas的excellent series

當條件確定後,您的函數將在某個分支處退出。唯一的問題是你不應該在倒數第二行中丟棄contain x r

可以爲清楚起見去除多餘if/else

let rec contains (x : #IComparable<'a>) = function 
    | Nil -> false 
    | Leaf y -> x.CompareTo(y) = 0 
    | Node(l, y, r) -> 
     match l, y, r with 
     | l, y, Nil -> x.CompareTo(y) = 0 || contains x l 
     | Nil,y, r -> x.CompareTo(y) = 0 || contains x r 
     | _ -> x.CompareTo(y) = 0 || contains x l || contains x r 
+0

感謝系列提:-)但應當指出,這樣做會影響到性能的 - 所以,如果你想要一些DSL更有用類似於您關心表達能力的代碼,而不是低級函數實現。 –

+0

是的,當性能很關鍵時,應該去定製遞歸函數。 – pad