Как сделать взаимно рекурсивные структуры в Javascript?
Мне было интересно, возможно ли иметь взаимно рекурсивные объекты в Javascript и, если да, то как?
Цель:
Я хочу иметь три объекта:
- Тот, который представляет
Boolean
тип с двумя значениямиTrue
а такжеFalse
- Тот, который представляет
True
объектBoolean
тип - Тот, который представляет
False
объектBoolean
тип
Хитрость в том, что я хочу спросить True
возражать его тип, и я должен вернуться Boolean
объект, и я хочу спросить Boolean
возражать его значения, и я должен получить 2 объекта: True
объект и False
объект.
Но он должен быть полностью рекурсивным в том смысле, что я получаю что-то вроде этого (хотя это не обязательно должно быть именно так):
True
// {name : "True", type : [Object object]}
False
// {name : "False", type : [Object object]}
Boolean
// {name : "Boolean", values : [Object object]}
Boolean.values
// {True: [Object object], False: [Object object]}
True.type
// {name : "Boolean", values : [Object object]}
False.type
// {name : "Boolean", values : [Object object]}
Boolean.values.True
// {name : "True", type: [Object object]}
Boolean.values.True.type
// {name : "Boolean", values : [Object object]}
Boolean.values.True.type.values
// {True : [Object object], False: [Object object]}
и так далее...
Если это помогает, они должны удовлетворять свойствам, которые:
Boolean === Boolean.values.True.type
Boolean === Boolean.values.True.type.values.True.type
True === Boolean.values.True
True === True.type.values.True.type.values.True.type.values.True
False === Boolean.values.False
False === True.type.values.False
и способность делать это должна быть бесконечной
Заметка
Это могут быть функции, а не объекты. И звонки не обязательно должны быть такими.
2 ответа
Ну вот:
//Define the top level objects but avoid recursion
var True = {};
var False = {};
var Boolean = {
values: {
True: True,
False: False
}
};
//Create the recursion
True.type = Boolean;
False.type = Boolean;
Это очень просто:
var Boolean = {
name: "Boolean",
values: {
True: {
name: "True"
},
False: {
name: "False"
}
}
};
var True = Boolean.values.True;
var False = Boolean.values.False;
True.type = Boolean;
False.type = Boolean;
Вы пытаетесь создать алгебраический тип данных?
Изменить: Вот как я бы создать алгебраический тип данных:
function data(constructors) {
var factory = function (constructor) {
this.constructor = constructor || this;
};
var type = factory.prototype = {};
for (var name in constructors) {
var fields = constructors[name];
if (fields) {
var body = [" var data = new " + name + "(arguments.callee);"];
var length = fields.length;
var params = [];
for (var i = 0; i < length; i++) {
var param = "arg" + i;
body.push(" data." + fields[i] + " = " + param + ";");
params.push(param);
}
body.unshift("return function (" + params.join(", ") + ") {");
body.push(" return data;", "};");
type[name] = Function(name, body.join("\n"))(factory);
} else type[name] = new factory;
}
return type;
}
Используя функцию данных, мы можем определить алгебраические типы данных следующим образом:
var Boolean = data({
True: null,
False: null
});
var True = Boolean.True;
var False = Boolean.False;
var List = data({
Nil: null,
Cons: ["head", "tail"]
});
var Nil = List.Nil;
var Cons = List.Cons;
Имеет следующие инварианты:
Object.getPrototypeOf(True) === Boolean;
Object.getPrototypeOf(False) === Boolean;
Object.getPrototypeOf(Nil) === List;
Object.getPrototypeOf(Cons(0, Nil)) === List;
True.constructor === True;
False.constructor === False;
Nil.constructor === Nil;
Cons(0, Nil).constructor === Cons;
Используя это, вы можете создавать чистые функции следующим образом:
List.map = function (f) {
switch (this.constructor) {
case Nil: return Nil;
case Cons:
var x = this.head;
var xs = this.tail;
return Cons(f(x), xs.map(f));
}
};
function map(f, a) {
return a.map(f);
}
Вы можете использовать его следующим образом:
function toList(a) {
var list = Nil;
for (var i = a.length - 1; i >= 0; i--) list = Cons(a[i], list);
return list;
}
var xs = toList([1,2,3]);
var ys = map(function (a) {
return a * 2;
}, xs);
Надеюсь, это поможет.