【问题标题】:How to get, resolve and verify transitive/cyclic dependency如何获取、解决和验证传递/循环依赖
【发布时间】:2020-02-12 13:21:45
【问题描述】:

我需要一些自定义方法来解析可能具有传递性的变量并验证循环/不可解析字段。 例如

var v1 = 'asd';
var v2 = '@@{v1}@@';
var v3 = '@@{v2}@@';
var v4 = '@@{v3}@@';
var v5 = '@@{v4}@@';
var v6 = '@@{v5}@@_@@{v3}@@';

var a1 = '@@{v1}@@_@@{a3}@@';
var a2 = '@@{a1}@@';
var a3 = '@@{a2}@@';

var x1 = 'asd';
var x2 = '@@{x1}@@';
var x3 = '@@{x1}@@_@@{x2}@@';

var p1 = '@@{v4}@@_@@{xyz}@@';
var p2 = '@@{xyz}@@';

const getVariableNames = str => {
    str += '';
    const variables = str.match(/(@@{[^}{)(\]\[\-+*\/]+?}@@)+?/g);
    return variables && variables.length ? variables.map(i => i.substring(3, i.length - 3)) : false;
};

const isCyclic = (myMap, node, target, [...visited] = []) => {
    if (myMap[node]) {
        if (node === target) {
            return true;
        }
        if (visited.includes(node) || !myMap[node].dep.usedIns) {
            return false;
        }
        visited.push(node);
        return myMap[node].dep.usedIns.some(n => isCyclic(myMap, n, target, visited));
    }
};

var allVars = [{ name: 'v1', val: v1 }, { name: 'v2', val: v2 },
    { name: 'v3', val: v3 }, { name: 'v4', val: v4 }, { name: 'v5', val: v5 }, { name: 'v6', val: v6 },
    { name: 'a1', val: a1 }, { name: 'a2', val: a2 }, { name: 'a3', val: a3 }, { name: 'x1', val: x1 },
    { name: 'x2', val: x2 }, { name: 'x3', val: x3 }];

function getValueMap(arr) {
    // need to get implemented
    var nodeAVL = [];
    const varNamesMap = arr.reduce((a, b) => {
        a[b.name] = { ...b, v: getVariableNames(b.val), dependsOn: [], usedIn: [], cyclic: [], unresolved: [] };
        return a;
    }, {});
    return varNamesMap;
}

var retVar = getValueMap(allVars);

console.log(retVar);
// should console
/* {
    v1: { val: 'asd', dependsOn: [], usedIn: ['v2', 'v3', 'v4', 'v5', 'v6', 'p1'], cyclic: [], unresolved: [] },
    v2: { val: 'asd', dependsOn: ['v1'], usedIn: ['v3', 'v4', 'v5', 'v6'], cyclic: [], unresolved: [] },
    v3: { val: 'asd', dependsOn: ['v2'], usedIn: ['v4', 'v5', 'v6'], cyclic: [], unresolved: [] },
    v4: { val: 'asd', dependsOn: ['v3'], usedIn: ['v5', 'v6'], cyclic: [], unresolved: [] },
    v5: { val: 'asd', dependsOn: ['v4'], usedIn: ['v6'], cyclic: [], unresolved: [] },
    v6: { val: 'asd_asd', dependsOn: ['v5', 'v3'], usedIn: [], cyclic: [], unresolved: [] },

    a1: { val: '', dependsOn: ['v1'], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },
    a2: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },
    a3: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },

    x1: { val: 'asd', dependsOn: [], usedIn: [], cyclic: [], unresolved: [] },
    x2: { val: 'asd', dependsOn: ['x1'], usedIn: ['x3'], cyclic: [], unresolved: [] },
    x3: { val: 'asd_asd', dependsOn: ['x1', 'x2'], usedIn: [], cyclic: [], unresolved: [] },

    p1: { val: '', dependsOn: ['v4'], usedIn: [], cyclic: [], unresolved: ['xyz'] },
    p2: { val: '', dependsOn: [], usedIn: [], cyclic: [], unresolved: ['xyz'] },
};*/

我的预期输出是

{
    v1: { val: 'asd', dependsOn: [], usedIn: ['v2', 'v3', 'v4', 'v5', 'v6', 'p1'], cyclic: [], unresolved: [] },
    v2: { val: 'asd', dependsOn: ['v1'], usedIn: ['v3', 'v4', 'v5', 'v6'], cyclic: [], unresolved: [] },
    v3: { val: 'asd', dependsOn: ['v2'], usedIn: ['v4', 'v5', 'v6'], cyclic: [], unresolved: [] },
    v4: { val: 'asd', dependsOn: ['v3'], usedIn: ['v5', 'v6'], cyclic: [], unresolved: [] },
    v5: { val: 'asd', dependsOn: ['v4'], usedIn: ['v6'], cyclic: [], unresolved: [] },
    v6: { val: 'asd_asd', dependsOn: ['v5', 'v3'], usedIn: [], cyclic: [], unresolved: [] },

    a1: { val: '', dependsOn: ['v1'], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },
    a2: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },
    a3: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },

    x1: { val: 'asd', dependsOn: [], usedIn: [], cyclic: [], unresolved: [] },
    x2: { val: 'asd', dependsOn: ['x1'], usedIn: ['x3'], cyclic: [], unresolved: [] },
    x3: { val: 'asd_asd', dependsOn: ['x1', 'x2'], usedIn: [], cyclic: [], unresolved: [] },

    p1: { val: '', dependsOn: ['v4'], usedIn: [], cyclic: [], unresolved: ['xyz'] },
    p2: { val: '', dependsOn: [], usedIn: [], cyclic: [], unresolved: ['xyz'] },
}

【问题讨论】:

  • 这个不清楚。例如,“可能有”和“验证”是模糊的。请在代码问题中提供minimal reproducible example--cut & paste & runnable code;具有期望和实际输出(包括逐字错误消息)的示例输入(作为初始化代码);标签和版本;明确的规范和解释。这包括您可以提供的最少代码,即您显示的代码可以通过您显示的代码扩展为不可以。 (调试基础。)缺少一个是关闭的理由,但不幸的是,我们不能在赏金开启时关闭。
  • 谢谢@philipxy,但我认为我基本上在这里误导了你“可能有”和“验证”这个词不用来使它模糊,但如果需要的情况不匹配,那么建议的解决方案可以有算法来制作性能更高,例如在我们的例子中,如果一组变量没有“传递/循环/不可解析”字段,则解决方案不需要执行复杂的任务(仅当解决方案的算法支持它时)。如果您觉得如此,欢迎编辑建议。将来可能会对我有所帮助。再次感谢
  • 请通过编辑而非 cmets 进行澄清。尽管您的评论不清楚并且没有澄清您的问题或解决我之前的评论。请采取行动。没有minimal reproducible example 的代码问题应该被关闭和投票,直到得到充分改进(并评论澄清)。不要指望我们预测你没有给予什么。 PS 我所说的模糊的意思是这些都是通用术语,但他们和你都没有解释激励他们的细节。

标签: javascript graph transitive-dependency cyclic-dependency


【解决方案1】:

这几乎输出了您的预期输出:

const v1 = 'asd';
const v2 = '@@{v1}@@';
const v3 = '@@{v2}@@';
const v4 = '@@{v3}@@';
const v5 = '@@{v4}@@';
const v6 = '@@{v5}@@_@@{v3}@@';

const a1 = '@@{v1}@@_@@{a3}@@';
const a2 = '@@{a1}@@';
const a3 = '@@{a2}@@';

const x1 = 'asd';
const x2 = '@@{x1}@@';
const x3 = '@@{x1}@@_@@{x2}@@';

const p1 = '@@{v4}@@_@@{xyz}@@';
const p2 = '@@{xyz}@@';

const getVariableNames = (str) => {
    str += '';
    const variables = str.match(/(@@{[^}{)(\][\-+*/]+?}@@)+?/g);
    return variables && variables.length ? variables.map(i => i.substring(3, i.length - 3)) : false;
};

const getVal = (str, arr) => arr.filter(v => v.name === str)[0].val;

const isCyclic = (varName, arr, visited = [varName]) => {
    const varNames = getVariableNames(getVal(varName, arr));
    if (!varNames) return false;
    if (varNames.some(n => getVariableNames(getVal(n, arr)) && getVariableNames(getVal(n, arr)).some(v => visited.includes(v)))) return true;
    return varNames.some(n => isCyclic(n, arr, visited));
};

const isUnresolved = (varName, arr) => !arr.filter(v => v.name === varName).length;

const resolve = (str, arr) => {
    const varNames = getVariableNames(str);
    if (varNames) varNames.forEach(n => str = str.replace(`@@{${n}}@@`, resolve(getVal(n, arr), arr)));
    return str;
};

const usedIn = (varName, map, arr) => {
    const result = [];
    Object.entries(map)
        .filter(([, value]) => value.dependsOn.includes(varName))
        .forEach(([name, value]) => {
            const varNames = getVariableNames(value.val);
            if (varNames && !value.unresolved.length) result.push(...usedIn(name, map, arr));
            else result.push(name);
        });
    return result;
};

const getValueMap = (arr) => {
    const varNamesMap = arr.reduce((acc, curr) => {
        let val = '';
        const dependsOn = [];
        const cyclic = [];
        const unresolved = [];

        const varNames = getVariableNames(curr.val);
        if (varNames) {
            varNames.forEach(varName => {
                if (isUnresolved(varName, arr) || isCyclic(varName, arr)) {
                    if (isUnresolved(varName, arr)) unresolved.push(varName);
                    else if (isCyclic(varName, arr)) cyclic.push(varName);
                    val = '';
                } else dependsOn.push(varName);
            });
        }

        if (!cyclic.length && !unresolved.length) val = resolve(curr.val, arr);

        acc[curr.name] = { val, dependsOn, usedIn: [], cyclic, unresolved };
        return acc;
    }, {});

    Object.keys(varNamesMap).forEach(name => varNamesMap[name].usedIn.push(...usedIn(name, varNamesMap, arr)));

    return varNamesMap;
};

const allVars = [
    { name: 'v1', val: v1 }, { name: 'v2', val: v2 }, { name: 'v3', val: v3 },
    { name: 'v4', val: v4 }, { name: 'v5', val: v5 }, { name: 'v6', val: v6 },
    { name: 'a1', val: a1 }, { name: 'a2', val: a2 }, { name: 'a3', val: a3 },
    { name: 'x1', val: x1 }, { name: 'x2', val: x2 }, { name: 'x3', val: x3 },
    { name: 'p1', val: p1 }, { name: 'p2', val: p2 }
];

console.log(getValueMap(allVars));

这个输出:

{
  v1: { val: 'asd', dependsOn: [], usedIn: ['v2', 'a1'], cyclic: [], unresolved: [] },
  v2: { val: 'asd', dependsOn: ['v1'], usedIn: ['v3'], cyclic: [], unresolved: [] },
  v3: { val: 'asd', dependsOn: ['v2'], usedIn: ['v4', 'v6'], cyclic: [], unresolved: [] },
  v4: { val: 'asd', dependsOn: ['v3'], usedIn: ['v5', 'p1'], cyclic: [], unresolved: [] },
  v5: { val: 'asd', dependsOn: ['v4'], usedIn: ['v6'], cyclic: [], unresolved: [] },
  v6: { val: 'asd_asd', dependsOn: ['v5', 'v3'], usedIn: [], cyclic: [], unresolved: [] },

  a1: { val: '', dependsOn: ['v1'], usedIn: [], cyclic: ['a3'], unresolved: [] },
  a2: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1'], unresolved: [] },
  a3: { val: '', dependsOn: [], usedIn: [], cyclic: ['a2'], unresolved: [] },

  x1: { val: 'asd', dependsOn: [], usedIn: ['x2', 'x3'], cyclic: [], unresolved: [] },
  x2: { val: 'asd', dependsOn: ['x1'], usedIn: ['x3'], cyclic: [], unresolved: [] },
  x3: { val: 'asd_asd', dependsOn: ['x1', 'x2'], usedIn: [], cyclic: [], unresolved: [] },

  p1: { val: '', dependsOn: ['v4'], usedIn: [], cyclic: [], unresolved: ['xyz'] },
  p2: { val: '', dependsOn: [], usedIn: [], cyclic: [], unresolved: ['xyz'] }
}

唯一的区别是:

  • usedIn 只有直接使用变量的变量(例如v3.usedIn[v4, v6] 而不是[v4, v5, v6]
  • cyclic 仅具有直接使用的循环变量(例如,a2.cyclic[a1] 而不是 [a1, a2, a3]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-15
    • 2016-09-21
    • 2020-11-12
    相关资源
    最近更新 更多