I need to define a data structure recursively in Javascript. Here is a simple example of a circular linked list:
// 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)));
When this is executed, head in List 'c' is resolved to undefined, not List 'a' as I need. List is an example function that returns a function (It is not an Javascript list that I can append to).
I tried to wrap the definition of head is a self executing named function, but that blew the stack when head was resolved.
What is the Javascript style solution that I am overlooking?
Attempt
Fooling around, I came up with some code that may work:
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
...
But this is really ugly. Any better solutions?
Solution
user1689607 came up with a good solution which I have encapsulated to hide some of the implementation:
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'
One more update, I ended up going with passing the self reference explicitly instead of changing the scope:
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)));
});
This code is used in parse.js .
This is what you want?
var headCaller = function() { return head.apply(this, arguments); };
var head = List('a', List('b', List('c', headCaller)));
It gives the result you seem to want...
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"
]
When you give head
in parameter, it's not yet assigned. You need to use two steps. Also, there is no need to use nested functions.
function List(f, r) {
return { value: f, next: r};
}
var head = List('a', List('b', List('c', null)));
head.next.next.next.next = head;
In Javascript, there are no pointer, but all Array
or Object
are assigned by reference.
EDIT:
A solution for make it simpler:
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
I am not sure if this is what you want, check it:
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
Added: a function to do it more easily
// 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']);
Demo: http://jsfiddle.net/alvinhochun/5nmpR/
In JavaScript, things are interesting. Everything is references, yet the reference itself is a value.
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.