(Отказ от ответственности: я немного сократил логику вашего кода. Может быть, я сделал ошибку, но общий смысл тот же.)
В большинстве случаев,нет, вы не должны использовать eval
в макросе.Есть две возможные альтернативы тому, что вы дали.Во-первых, если вам требуется, чтобы макрос работал только с литеральными логическими значениями (т. Е. Со значениями true
и false
), то они хранятся прямо в AST, и вы можете выполнять обычные вычисления непосредственно во время компиляции:
julia> macro make_clause_literal(xs, bools, res)
clause_elements = map((lit, arg) -> lit == arg, bools.args, xs.args)
res && all(clause_elements)
end
@make_clause_literal (macro with 1 method)
julia> @macroexpand @make_clause_literal((false, false), (false, false), true)
true
Должна быть добавлена некоторая проверка, если входные данные действительно являются литеральными логическими значениями.
С другой стороны, если вы хотите поместить другие выражения в лунку, преобразуйте код во что-то эффективное, чтоделает то же самое и оставляет оценку во время выполнения:
julia> macro make_clause(xs, bools, res)
clause_elements = map((lit, arg) -> :($lit == $arg), bools.args, xs.args)
esc(:($res && $(foldr((e,f) -> :($e && $f), clause_elements))))
end
@make_clause (macro with 1 method)
julia> @macroexpand @make_clause((false, false), (false, false), true)
:(true && (false == false && false == false))
julia> @macroexpand @make_clause((false, false), (false, x), y)
:(y && (false == false && x == false))
Построение последовательности &&
должно быть таким же хорошим, как и в плане избежания промежуточных массивов и короткого замыкания.
Третий вариант, который я бы порекомендовал, - это написать обычную функцию времени выполнения, выполняющую оценку предложения, и переписать любой из вышеупомянутых макросов в терминах вызова этого.Я оставляю это как упражнение.Вы также можете сделать комбинацию обоих подходов и оценить выражение во время компиляции, насколько это возможно, но я думаю, что компилятор уже сделает это в некоторой степени.