javascript中的功能数据类型

时间:2012-06-27 14:45:21

标签: javascript types functional-programming

如何在javascript中构建函数和递归数据类型?

我希望能够做像ML这样的事情:

datatype binary_node = Node of binary_node*binary_node 
                     | Lead of int

前段时间我参加了函数式编程课程 - 由于一些随机的原因,这个课程在Scheme中,我们通过制作数据来构建数据类型,从数据类型的名称开始,然后是“有效负载”,这是在Javascript中进行函数式编程风格数据类型的方法吗?

construct_node(n1,n2) -> 
    ("Node", n1, n2).

construct_leaf(int_value) ->
    ("Leaf", int_value).

然后是类型检查器:

is_node(n) ->
    if (n[0] == "Node") ->
        is_binary_tree(n[1])  and is_binary_tree(n[2])
    else
        false
is_leaf(l) ->
    if(l[0] == "Leaf") ->
        is_integer(n[1])
    else 
        false
is_binary_tree(t) ->
    is_node(t) or is_leaf(t)

在javascript中执行此操作的最明智的方法是什么?

3 个答案:

答案 0 :(得分:2)

JavaScript通常以duck typed方式使用。这样,您就不需要定义任何特殊的数据类型。具有属性node1node2的任何对象都可以被视为二叉树节点。

var n = {
    node1: {
        node1: 456,
        node2: 789
    },
    node2: 1002
};

function isNode(x) {
    return x && isBinaryTree(x.node1) && isBinaryTree(x.node2);
}

function isLeaf(x) {
    return typeof x === 'number';
}

function isBinaryTree(x) {
    return isNode(x) || isLeaf(x);
}

请注意,上述函数用于递归检查整个树的完整性,而不是在遍历期间逐个节点区分。

答案 1 :(得分:1)

我看到的一种方法是为代数数据类型的每个案例创建一个构造函数,并使用instanceof测试来实现分支。例如,这就是Roy language实现模式匹配和标记联合的方式。

var Node = function(a, b){
    //protect against people forgetting to use "new" to call the constructor:
    if(!(this instanceof Node)){ return new Node(a, b) }

    //optionaly do extra type checking, if that is your thing:
    if(!(a instanceof Leaf)){ /*...*/ }
    if(!(b instanceof Leaf)){ /*...*/ }

    //save properties
    this.node1 = a;
    this.node2 = b;
};

var Leaf = function(value){
    /*...*/
    this.value = value;
};

这样,标记内部的“__proto__”属性,有效负载是对象中常用的实例。

我喜欢这种方法的一个原因是它非常“安全”。内部原型属性不可编辑,并且使用构造函数或对象(而不是符号或字符串)使得它更少受到名称冲突的影响,具有不同的类型或对象的属性。

另一个好处是,与依赖于鸭子类型不同,这种方法也可以与枚举和其他不同情况具有相同属性集的情况无缝协作。

关于Javascript的一个坏处是,就像LISP一样,它没有内置的解构支持,所以你可能想要创建一个自定义匹配函数,如下所示。

var match_tree = function(x, cases){
    //just a helper function to make things pretty
    if(x instanceof Node){
        return cases.node.call(this, x.node1, x.node2);
    }else if(x instanceof Leaf){
        return cases.leaf.call(this, x.value);
    }else{
        throw new TypeError("pattern match failed");
    }
};

var sum_leaves = function(tree){
    return match_tree(tree, {
        node: function(val){ return val },
        leaf: function(left, right){
           return sum_leaves(left) + sum_leaves(right);
        }
    });
 };

答案 2 :(得分:1)

我是一种名为Roy的语言的创造者。 Roy是一种具有静态类型,代数数据类型和模式匹配的语言。它也编译成JavaScript。

你的例子看起来像这样:

data Tree = Node Tree Tree | Leaf Number

Number是内置的JavaScript类型。

现在我们可以在ADT上进行模式匹配:

let isNode n = match n
  case (Node a b) = true
  case (Leaf l) = false

let isLeaf l = match l
  case (Leaf l) = true
  case (Node a b) = false

这是JavaScript输出:

var Node = function(Tree_0, Tree_1) {
    if(!(this instanceof Node)) {
        return new Node(Tree_0, Tree_1);
    }
    this._0 = Tree_0;
    this._1 = Tree_1;
};
var Leaf = function(Number_0) {
    if(!(this instanceof Leaf)) {
        return new Leaf(Number_0);
    }
    this._0 = Number_0;
};
var isNode = function(n) {
    return (function() {
        if(n instanceof Node) {
            var a = n._0;
            var b = n._1;
            return true;
        } else if(n instanceof Leaf) {
            var l = n._0;
            return false;
        }
    })();
};
var isLeaf = function(l) {
    return (function() {
        if(l instanceof Leaf) {
            var l = l._0;
            return true;
        } else if(l instanceof Node) {
            var a = l._0;
            var b = l._1;
            return false;
        }
    })();
};