如何通过 S 表达式树来满足测试用例

How to make your way through an S-expression tree to satisfy a test case

所以我有了这个 S 表达式数组

  const condition = [
  'OR',
  [
    'AND',
    ['==', '$State', 'Alabama'],
    ['==', '$Profession', 'Software development']
  ],
  ['==', '$Undefined', ''],
  [
    'AND',
    ['==', '$State', 'Texas']
  ],
  [
    'OR',
    [
      'OR',
      ['==', '$Profession', 'Tradesperson']
    ]
  ]
]

我有这个测试用例和功能需要满足

const testCases = [
  [{'State': 'Alabama', 'Profession': 'Software development'}, true],
  [{'State': 'Texas'}, true],
  [{'State': 'Alabama', 'Profession': 'Gaming'}, false],
  [{'State': 'Utah'}, false],
  [{'Profession': 'Town crier'}, false],
  [{'Profession': 'Tradesperson'}, true],
  [{}, false]
]

for (const [index, [context, expected]] of testCases.entries()) {
  console.log(
    evaluate(condition as Condition, context) === expected
      ? `${index} ok`
      : `${index} FAIL`
  )
}

我目前所掌握的是

function evaluate (condition: Condition, context: Context): boolean {
  if (isLogicalCondition(condition)) {
    const [operator, ...conditions] = condition

  }

  if (isComparisonCondition(condition)) {
    const [operator, variable, value] = condition
    
  }

  return false
}

函数、类型和变量是

type Context = {
  [k: string]: string | undefined
}

enum LogicalOperator {
  And = 'AND',
  Or = 'OR'
}

enum ComparisonOperator {
  Eq = '=='
}

type Operator = LogicalOperator | ComparisonOperator 
type LogicalCondition = [LogicalOperator, ...Array<Condition>]
type Variable = string
type Value = string
type ComparisonCondition = [ComparisonOperator, Variable, Value]
type Condition = LogicalCondition | ComparisonCondition

function isLogicalCondition (condition: Condition): condition is LogicalCondition {
  return Object.values(LogicalOperator).includes(condition[0] as LogicalOperator)
}

function isComparisonCondition (condition: Condition): condition is ComparisonCondition {
  return Object.values(ComparisonOperator).includes(condition[0] as ComparisonOperator)
}

我的问题是我如何以一种足够抽象的方式来考虑这个问题来解决这个问题以满足测试而不对测试结果进行硬编码?我完全迷路了...

你应该使用递归。 “OR”运算符转换为 some 方法调用,“AND”转换为 every 方法调用。

为了相等,您应该处理任何“$”前缀,在这种情况下,您必须在上下文对象中查找值。最好不要假设第一个参数总是有“$”...,所以我建议在 both 参数上使用映射器,每次处理潜在的“$ ".

你可以使用这个功能:

function evaluate(condition, context) {
    let [operator, ...arguments] = condition;
    if (operator === "==") {
        return arguments.map(arg => arg[0] === "$" ? context[arg.slice(1)] : arg)
                        .reduce(Object.is);
    }
    if (operator === "OR") {
        return arguments.some(argument => evaluate(argument, context));
    }
    if (operator === "AND") {
        return arguments.every(argument => evaluate(argument, context));
    }
    throw "unknown operator " + operator;
}

运行 个测试用例:

function evaluate(condition, context) {
    let [operator, ...arguments] = condition;
    if (operator === "==") {
        return arguments.map(arg => arg[0] === "$" ? context[arg.slice(1)] : arg)
                        .reduce(Object.is);
    }
    if (operator === "OR") {
        return arguments.some(argument => evaluate(argument, context));
    }
    if (operator === "AND") {
        return arguments.every(argument => evaluate(argument, context));
    }
    throw "unknown operator " + operator;
}


const condition = [
  'OR',
  [
    'AND',
    ['==', '$State', 'Alabama'],
    ['==', '$Profession', 'Software development']
  ],
  ['==', '$Undefined', ''],
  [
    'AND',
    ['==', '$State', 'Texas']
  ],
  [
    'OR',
    [
      'OR',
      ['==', '$Profession', 'Tradesperson']
    ]
  ]
]

const testCases = [
  [{'State': 'Alabama', 'Profession': 'Software development'}, true],
  [{'State': 'Texas'}, true],
  [{'State': 'Alabama', 'Profession': 'Gaming'}, false],
  [{'State': 'Utah'}, false],
  [{'Profession': 'Town crier'}, false],
  [{'Profession': 'Tradesperson'}, true],
  [{}, false]
]

for (const [index, [context, expected]] of testCases.entries()) {
  console.log(
    evaluate(condition, context) === expected
      ? `${index} ok`
      : `${index} FAIL`
  )
}