Javascript递归数据结构定义

时间:2012-11-10 16:44:59

标签: javascript recursion

我需要在Javascript中递归地定义数据结构。以下是循环链表的简单示例:

// List a very simplified example of what the actual (non list) code does.
function List(f, r) {
    return function(){ return [f, r]; };
}

var first = function (l){ return l()[0]; }
var rest = function (l){ return l()[1]; }

var head = List('a', List('b', List('c', head)));

执行此操作时,列表'c'中的head被解析为undefined,而不是List'a',因为我需要。 List是一个返回函数的示例函数(它不是我可以附加到的Javascript列表)。

我试图包装head的定义是一个自动执行的命名函数,但是当头被解析时,它会破坏堆栈。

我忽略了什么是Javascript风格的解决方案?


尝试

愚弄,我想出了一些可行的代码:

var f = function(){
    var value;
    return function(v){
        if (value === undefined)
            value = v
        return value.apply(undefined, arguments);
    };
};

var tempHead = f();
var head = List('a', List('b', List('c', tempHead)));
tempHead(head);

first(head); // a
first(rest(head)) // b
first(rest(rest(head))) // c
first(rest(rest(rest(head)))) // a
first(rest(rest(rest(rest(head))))) // b
...

但这真的很难看。有更好的解决方案吗?


解决方案

user1689607提出了一个很好的解决方案,我已将其封装起来隐藏了一些实现:

var def = function(name, impl) {
    var value;
    return value = impl.apply(Object.defineProperty({}, name, {
       'value': function() { return value.apply(this, arguments); }
    }));
};

function List(f, r) {
    return function(){ return [f, r]; };
}

function first(l){ return l()[0]; }
function rest(l){ return l()[1]; }

var circle = def('head', function() {
    return List('a', List('b', List('c', this.head)));
});

first(circle); // 'a'
first(rest(circle)); // 'b'
first(rest(rest(circle))); // 'c'
first(rest(rest(rest(circle)))); // 'a'
first(rest(rest(rest(rest(circle))))); // 'b'

还有一次更新,我最后明确地传递了自引用而不是更改范围:

var def = function(impl) {
    var value;
    return (value = impl(function() { return value.apply(this, arguments); }));
};

var circle = def(function(self) {
    return List('a', List('b', List('c', self)));
});

此代码用于parse.js

3 个答案:

答案 0 :(得分:1)

这是你想要的吗?

var headCaller = function() { return head.apply(this, arguments); };

var head = List('a', List('b', List('c', headCaller)));

它给出了你似乎想要的结果......

DEMO: http://jsfiddle.net/ruNY3/

var results = [
    first(head), // a
    first(rest(head)), // b
    first(rest(rest(head))), // c
    first(rest(rest(rest(head)))), // a
    first(rest(rest(rest(rest(head))))) // b
];

[
    "a",
    "b",
    "c",
    "a",
    "b"
]

答案 1 :(得分:0)

当您在参数中提供head时,它尚未分配。 您需要使用两个步骤。 此外,不需要使用嵌套函数。

function List(f, r) {
    return { value: f, next: r};
}

var head = List('a', List('b', List('c', null)));
head.next.next.next.next = head;

在Javascript中,没有指针,但所有ArrayObject都是通过引用分配的。


修改

使解决方案更简单的方法:

function List(f) {
    var list = { value: f, next: null };
    list.next = list;
    return list;
}

function insert(list, elm) {
    list.next = { next: list.next, value: elm };
}

function forward(list, nb) {
    for (var current = list; nb > 0; nb--)
        current = current.next;
    return current;
}

var head = List('a');
insert(head, 'b');
insert(head.next, 'c');

console.log(head.value); //a
console.log(head.next.value); //b
console.log(head.value); //c

console.log(forward(head, 3));//a

答案 2 :(得分:0)

我不确定这是否是您想要的,请检查:

function List(f, r) {
    var fr = [f, r];
    var func = function() {
        return fr;
    };
    func.fr = fr; // because that is a reference we can trick it
    return func;
}

var head = List('a', List('b', List('c', null)));
head.fr[1].fr[1].fr[1] = head; // make a loop

添加:更容易实现的功能

// Or this method for lazy people
function createCircularList(f) {
    var head = List(f[f.length - 1], null);
    var firstfr = head.fr;
    for (var i = f.length - 2; i >= 0; i--) {
        head = List(f[i], head);
    }
    firstfr[1] = head;
    return head;
}

var head = createCircularList(['a', 'b', 'c']);

演示:http://jsfiddle.net/alvinhochun/5nmpR/

在JavaScript中,事情很有趣。一切都是参考,但参考本身就是一个价值。

var a = [0, 0];    // `a` is set to the reference of `[0, 0]`
var b = a;         // `b` refers to the same thing as `a`
console.log(b[0]); // shows 0
a[0] = 1;          // it modifies the object that it references to
console.log(b[0]); // shows 1
a = [2, 0];        // a new reference is assigned to `a`
console.log(b[0]); // still shows 1