What is a catamorphism and can it be implemented in C# 3.0?

后端 未结 5 710
闹比i
闹比i 2020-12-04 13:22

I\'m trying to learn about catamorphisms and I\'ve read the Wikipedia article and the first couple posts in the series of the topic for F# on the Inside F# blog.

5条回答
  •  不知归路
    2020-12-04 14:18

    Brian's answer in the first paragraph is correct. But his code example doesn't really reflect how one would solve similar problems in a C# style. Consider a simple class node:

    class Node {
      public Node Left;
      public Node Right;
      public int value;
      public Node(int v = 0, Node left = null, Node right = null) {
        value = v;
        Left = left;
        Right = right;
      }
    }
    

    With this we can create a tree in main:

    var Tree = 
        new Node(4,
          new Node(2, 
            new Node(1),
            new Node(3)
          ),
          new Node(6,
            new Node(5),
            new Node(7)
          )
        );
    

    We define a generic fold function in Node's namespace:

    public static R fold(
      Func combine,
      R leaf_value,
      Node tree) {
    
      if (tree == null) return leaf_value;
    
      return 
        combine(
          tree.value, 
          fold(combine, leaf_value, tree.Left),
          fold(combine, leaf_value, tree.Right)
        );
    }
    

    For catamorphisms we should specify the states of data, Nodes can be null, or have children. The generic parameters determine what we do in either case. Notice the iteration strategy(in this case recursion) is hidden inside the fold function.

    Now instead of writing:

    public static int Sum_Tree(Node tree){
      if (tree == null) return 0;
      var accumulated = tree.value;
      accumulated += Sum_Tree(tree.Left);
      accumulated += Sum_Tree(tree.Right);
      return accumulated; 
    }
    

    We can write

    public static int sum_tree_fold(Node tree) {
      return Node.fold(
        (x, l, r) => x + l + r,
        0,
        tree
      );
    }
    

    Elegant, simple, type checked, maintainable, etc. Easy to use Console.WriteLine(Node.Sum_Tree(Tree));.

    It's easy to add new functionality:

    public static List In_Order_fold(Node tree) {
      return Node.fold(
        (x, l, r) => {
          var tree_list = new List();
          tree_list.Add(x);
          tree_list.InsertRange(0, l);
          tree_list.AddRange(r);
          return tree_list;
        },
        new List(),
        tree
      );
    }
    public static int Height_fold(Node tree) {
      return Node.fold(
        (x, l, r) => 1 + Math.Max(l, r),
        0,
        tree
      );
    }
    

    F# wins in the conciseness category for In_Order_fold but that's to be expected when the language provides dedicated operators for constructing and using lists.

    The dramatic difference between C# and F# seems to be due to F#'s use of closures, to act as implicit data structures, for triggering the tail call optimization. The example in Brian's answer also takes in to account optimizations in F#, for dodging reconstructing the tree. I'm not sure C# supports the tail call optimization, and maybe In_Order_fold could be written better, but neither of these points are relevant when discussing how expressive C# is when dealing with these Catamorphisms.

    When translating code between languages, you need to understand the core idea of the technique, and then implement the idea in terms of the language's primitives.

    Maybe now you'll be able to convince your C# co-workers to take folds more seriously.

提交回复
热议问题