#javascript #s-expression
#javascript #s-выражение
Вопрос:
Итак, у меня есть этот массив 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)
}
Мой вопрос в том, как мне даже подумать об этом достаточно абстрактно, чтобы решить эту проблему, чтобы выполнить тест без жесткого кодирования результатов теста? Я полностью потерян…
Ответ №1:
Вы должны использовать рекурсию. Оператор «ИЛИ» преобразуется в some
вызов метода, а «И» — в every
вызов метода.
Для равенства вы должны иметь дело с любым префиксом «$», и в этом случае вам нужно искать значение в объекте контекста. Было бы неплохо не предполагать, что первый аргумент всегда имеет «$» …, поэтому я предлагаю использовать mapper для обоих аргументов, каждый раз имея дело с потенциальным «$».
Вы могли бы использовать эту функцию:
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`
)
}