我有一些表达列表,

const expressions = [
    'A&(B|C)',
    'A|(B&C)',
    'A|(B&(C|D))',
    'A|(B&C&D)',
];

从中,我需要得到这样的输出:

[[A, B], [A, C]]
[[A], [B, C]]
[A, [B, C], [B, D]]
[A, [B, C, D]] 

我try 用下面的方式做到这一点,但得到的是空数组,而不是正确的结果.

class ParsedExpression {
    operator: string | null;
    variables: string[];
    subExpressions: ParsedExpression[];

    constructor(operator: string | null, variables: string[], subExpressions: ParsedExpression[]) {
        this.operator = operator;
        this.variables = variables;
        this.subExpressions = subExpressions;
    }
}

function parseExpression(expression: string): ParsedExpression {
    const stack: ParsedExpression[] = [];
    let currentExpr: ParsedExpression | null = null;
    let currentVariable = '';

    for (const char of expression) {
        if (char === '(') {
            if (currentExpr !== null) {
                stack.push(currentExpr);
            }
            currentExpr = null;
        } else if (char === ')') {
            if (stack.length > 0) {
                const prevExpr = stack.pop();
                if (prevExpr !== null && currentExpr !== null) {
                    prevExpr.subExpressions.push(currentExpr);
                    currentExpr = prevExpr;
                }
            }
        } else if (char === '&' || char === '|') {
            if (currentExpr === null) {
                currentExpr = new ParsedExpression(char, [], []);
            } else {
                currentExpr.operator = char;
            }
        } else {
            if (currentExpr === null) {
                currentExpr = new ParsedExpression(null, [], []);
            }
            currentExpr.variables.push(char);
        }
    }

    return currentExpr as ParsedExpression;
}

function generateCombinations(parsedExpr: ParsedExpression): string[][] {
    if (!parsedExpr.operator) {
        return [parsedExpr.variables];
    }

    const leftCombinations = parsedExpr.subExpressions.length > 0 ? generateCombinations(parsedExpr.subExpressions[0]) : [[]];
    const rightCombinations = parsedExpr.subExpressions.length > 1 ? generateCombinations(parsedExpr.subExpressions[1]) : [[]];

    const combinations: string[][] = [];
    for (const left of leftCombinations) {
        for (const right of rightCombinations) {
            combinations.push(left.concat(right));
        }
    }

    return combinations;
}

const expressions = [
    'A&(B|C)',
    'A|(B&C)',
    'A|(B&(C|D))',
    'A|(B&C&D)',
];

for (const expr of expressions) {
    const parsedExpression = parseExpression(expr);
    const combinations = generateCombinations(parsedExpression);
    console.log(`Expression: ${expr}`);
    console.log(`Combinations: ${JSON.stringify(combinations)}`);
    console.log('-------------------');
}

我得到的输出,

Expression: A&(B|C)
Combinations: [[]]
-------------------
Expression: A|(B&C)
Combinations: [[]]
-------------------
Expression: A|(B&(C|D))
Combinations: [[]]
-------------------
Expression: A|(B&C&D)
Combinations: [[]]
-------------------

无论如何要解决这个问题?

推荐答案

generateCombinations函数不执行与任务相关的任何操作.它实际上应该将树压平为两个级别,其中最高级别是disjitors,第二级别由连词组成.

您可以使用回归来做到这一点,并在退出回归时压平.

我 Select 了一个实现,其中解析器允许括号是可选的,在这种情况下,&优先于|,并且变量名称可以由多个字符组成.解析器构建一棵二元树.我不会将变量存储在单独的数组中,而是允许子变量是变量(字符串)或 node .

在第二阶段,树可以自下而上地转换为数组数组,其中这个嵌套数组将表示CNF(合取范式)或DNF(合取范式).此表示需要从一种表示转换到另一种表示才能合并 node 的子 node .这种转换只不过是一个笛卡儿积.

最后,可以应用一些简化,以便底层数组只有唯一的值可以添加更多简化规则(正如我添加的额外测试用例中可以看到的那样),但我没有进一步追求这一点.

下面是一个JavaScript实现.添加打字应该不难:

class BinaryNode {
    constructor(value, left, right) {
        this.value = value;
        this.left = left;
        this.right = right;
    }
}

function parseToBinaryTree(expression) {
    const precedence = { "&": 2, "|": 1, "(": -1, ")": 0, "": 0 };
    const operators = [];
    const operands = [];
    for (const token of expression.match(/[^&|()\s]+|\S?/gs)) {
        if (token in precedence) {
            if (token !== "(") {
                while (precedence[operators.at(-1)] >= precedence[token]) {
                    if (operands.length < 2) throw new Error("Parser: Syntax error: operands length < 2"); 
                    operands.push(new BinaryNode(operators.pop(), ...operands.splice(-2)));
                }
            }
            if (token && token !== ")") operators.push(token);
            else operators.pop(); // "("
        } else {
            operands.push(token);
        }
    }
    if (operands.length != 1) throw new Error("Parser: Syntax error: left over");
    return operands.pop();
}

const unique = values => [...new Set(values)];
const uniqueEach = arrays => arrays.map(unique);
const unwrapSingle = values => values.length === 1 ? values[0] : values;
const unwrapSingleEach = arrays => arrays.map(unwrapSingle);

function product(arrays) {
    if (arrays.length === 0) {
        return [[]];
    }
    const result = [];
    for (const x of arrays[0]) {
        for (const rest of product(arrays.slice(1))) {
            result.push([x, ...rest]);
        }
    }
    return result;
}

function normalize(root, operator="|", tab="") {
    if (typeof root === "string") { // Leaf node
        return [[root]];
    }
    const terms = uniqueEach([
        ...normalize(root.left, root.value),
        ...normalize(root.right, root.value)
    ]);
    if (root.value !== operator) {
        return uniqueEach(product(terms));
    }
    return terms;
}

const expressions = [
    'A&(B|C)',
    'A|(B&C)',
    'A|(B&(C|D))',
    'A|(B&C&D)',
    'A&(B|C)&(C|D)',
];

for (const expr of expressions) {
    const root = parseToBinaryTree(expr);
    const disj = unwrapSingleEach(normalize(root));
    console.log(`Expression: ${expr}`);
    console.log(JSON.stringify(disj));
    console.log('-------------------');
}

Typescript相关问答推荐

如何使用属性作为具有Generics的TypScript类中的类型守护?

泛型和数组:为什么我最终使用了`泛型T []`而不是`泛型T []`?<><>

泛型函数中输入参数类型的推断问题

类型,其中一条记录为字符串,其他记录为指定的

<;T扩展布尔值>;

Angular 17子路由

扩展参数必须具有元组类型或传递给REST参数

垫表页脚角v15

对于始终仅在不是对象属性时才抛出的函数,返回Never

不带其他属性的精确函数返回类型

如何缩小函数的返回类型?

自定义 Select 组件的类型问题:使用带逗号的泛型类型<;T与不带逗号的<;T&>;时出错

map 未显示控制台未显示任何错误@reaction-google-map/api

如何创建内部和外部组件都作为props 传入的包装器组件?

如何正确地将元组表格输入到对象数组实用函数(如ZIP)中,避免在TypeScrip 5.2.2中合并所有值

类型脚本中函数重载中的类型推断问题

Typescript泛型-键入对象时保持推理

递归地排除/省略两种类型之间的公共属性

为什么我无法在 TypeScript 中重新分配函数?

const 使用条件类型时,通用类型参数不会推断为 const