将函数应用于F#中的自定义类型

时间:2010-03-12 14:00:44

标签: f# functional-programming

在我学习F#的过程中,我遇到了一个我无法解决的问题。我已经定义了一个自定义类型:

type BinTree = 
  | Node of int * BinTree * BinTree
  | Empty

我创建了一个函数,它接受一棵树,遍历它,并将它访问的元素添加到列表中,然后返回它:

let rec inOrder tree = 
 seq{
 match tree with
  | Node (data, left, right) ->
   yield! inOrder left
   yield  data;
   yield! inOrder right
  | Empty -> ()
 }
 |> Seq.to_list;

现在我想创建一个类似于此的函数,它接受树和函数,遍历它并将函数应用于每个节点,然后返回树:

mapInOrder : ('a -> 'b) -> 'a BinTree -> 'b BinTree

这似乎很容易,而且可能是!但我不知道如何归还树。我试过这个:

let rec mapInOrder f tree = 
 match tree with
 | Node(data, left, right) ->
  mapInOrder f left
  Node(f(data), left, right)
  mapInOrder f right
 | Empty -> ()

但这会返回一个单位。我之前没有使用自定义类型,所以我可能在那里遗漏了一些东西!

2 个答案:

答案 0 :(得分:8)

试试这个:

let rec mapInOrder f = function
  | Node(a,l,r) ->
      let newL = mapInOrder f l
      let b = f a
      let newR = mapInOrder f r
      Node(b,newL,newR)
  | Empty -> Empty

如果该函数没有副作用,那么遍历顺序并不重要,您可以改为:

let rec map f = function
  | Node(a,l,r) -> Node(f a, map f l, map f r)
  | Empty -> Empty

答案 1 :(得分:2)

匹配是表达式。它返回匹配大小写的值。这意味着所有匹配案例必须具有相同的类型。匹配表达式本身就具有该类型。

在您的第一次尝试中,您的Empty子句返回(),因此具有单元类型 - 而不是您要查找的树类型。

由于mapInOrder只返回匹配结果,因此它也采用了单位返回类型。

Node子句很好,因为它的返回值是调用mapInOrder的结果,所以它也采用了单元类型,并且要求所有匹配子句都具有相同的类型。

kvb建议的一个关键变化是使Empty子句返回树而不是单位。一旦这样做,就会出现编译器错误和警告,指出其他问题。

您可以经常通过显式编码您喜欢的类型来解决此类问题,然后查看编译错误和警告的显示位置。