1

我需要在 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'中的头被解析为未定义,而不是我需要的列表'a'。List 是一个返回函数的示例函数(它不是我可以附加到的 Javascript 列表)。

我试图将 head 的定义包装为一个自执行的命名函数,但是当 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中使用。

4

3 回答 3

1

这就是你想要的?

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

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

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

演示: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"
]
于 2012-11-10T17:42:56.513 回答
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 中,没有指针,但所有的ArrayorObject都是通过引用分配的。


编辑:

使其更简单的解决方案:

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
于 2012-11-10T17:26:30.577 回答
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
于 2012-11-10T17:37:20.673 回答