Является ли вложение макросов в макросы плохой практикой? - PullRequest
0 голосов
/ 09 декабря 2018

Для иллюстрации допустим, у меня есть следующие макросы, вычисляющие строки в таблице истинности:

macro bool_to_lit(a)
    eval(a) ? (x -> x) : (x -> !x)
end

macro make_clause(xs, bools, res)
    lits = map((x -> @eval @bool_to_lit $x), bools.args)
    clause_elements = map.(lits, xs.args)
    and_res = all(push!(clause_elements, res))
    return and_res
end

#@make_clause((false, false), (false, false), true) returns true

@bool_to_lit возвращает замыкание в зависимости от значения его аргумента, а @make_clause используетрезультат для вычисления собственных значений.Однако, так как @make_clause использует @eval, я понимаю, что на самом деле он запускает @bool_to_lit (и, следовательно, не просто выполняет синтаксическое преобразование).

Будет ли это лучше (как в более быстром и генерирующем очистителе)код), чтобы избежать использования вложенного @eval в таких случаях, как, например, такой, что весь результат всего макродерева оценивается только один раз в rutime?

Это компромисс между более простым кодированием (т. е. обработка вложенных макросов как функций при использовании @eval) против правильности ( т.е. только синтаксические преобразования во время компиляции при исключении вложенных @eval)?

1 Ответ

0 голосов
/ 09 декабря 2018

(Отказ от ответственности: я немного сократил логику вашего кода. Может быть, я сделал ошибку, но общий смысл тот же.)

В большинстве случаев,нет, вы не должны использовать 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))

Построение последовательности && должно быть таким же хорошим, как и в плане избежания промежуточных массивов и короткого замыкания.

Третий вариант, который я бы порекомендовал, - это написать обычную функцию времени выполнения, выполняющую оценку предложения, и переписать любой из вышеупомянутых макросов в терминах вызова этого.Я оставляю это как упражнение.Вы также можете сделать комбинацию обоих подходов и оценить выражение во время компиляции, насколько это возможно, но я думаю, что компилятор уже сделает это в некоторой степени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...