Как заблокировать символы, не оценивая их?
Предположим, у меня есть список имен Symbol
s:
f1 := Print["f1 is evaluated!"];
list = {"f1", "f2"};
Очевидный способ Block
этих Symbol
приводит к их оценке:
In[19]:= With[{list=Symbol/@list},Block[list,f1//ToString]]
During evaluation of In[19]:= f1 is evaluated!
During evaluation of In[19]:= f1 is evaluated!
Out[19]= Null
Но без оценки мы могли бы Block
их без проблем:
In[20]:= Block[{f1, f2}, f1 // ToString]
Out[20]= "f1"
Можно ли внедрить этот список в область Block
без оценки Symbol
s?
Ответы
Ответ 1
Вот еще один способ сделать это:
SetAttributes[blockAlt,HoldRest];
blockAlt[s : {__String}, body_] :=
Replace[Join @@ ToHeldExpression[s], Hold[x__] :> Block[{x}, body]]
Мы сохраняем здесь на чистых функциях из-за разрушительного характера правил (они не уважают другие конструктивные области, включая самих себя)
ИЗМЕНИТЬ
Еще одна альтернатива (еще короче):
SetAttributes[blockAlt1, HoldRest];
blockAlt1[s : {__String}, body_] :=
Block @@ Append[[email protected][s], Unevaluated[body]]
Ответ 2
Отказ от ответственности. Хотя мой ответ обеспечивает решение проблемы, как было выражено, я не рекомендую его для регулярного использования. Я предлагаю это, потому что это может быть академическим интересом.
Время от времени, обычно в контексте отладки, я с нетерпением смотрел на Lisp MACROEXPAND-1
и хотел, чтобы функция Mathematica применяла только один уровень оценки к ее аргументу. Позвольте назвать эту мифическую функцию EvaluateOnce
. Он найдет правило преобразования, применимое к выражению, и применит только это правило, примерно так:
In[19]:= fact[0] = 1; fact[x_] := x * fact[x - 1]
EvaluateOnce[fact[5]]
Out[19]= Hold[5 fact[5-1]]
In[20]:= f1 := Print["f1 is evaluated!"];
EvaluateOnce[Symbol["f1"]]
Out[20]= Hold[f1]
Он будет работать и с несколькими выражениями:
In[21]:= EvaluateOnce[1 + 2 * 3, Sqrt @ Sin @ Pi]
Out[22]= Hold[1+6, Sqrt[0]]
Текущий вопрос может извлечь выгоду из такой возможности, тогда решение может быть выражено как:
EvaluateOnce @@ Symbol /@ Hold @@ list /.
Hold[args__] :> Block[{args}, f1 // ToString]
Увы, существует ряд технических препятствий для написания такой функции, и не в последнюю очередь это определенная доля размытости в отношении того, что именно представляет собой "единый уровень оценки" в Mathematica. Но дураки спешат туда, где ангелы боятся ступить, поэтому я предлагаю этот хак:
[email protected]
SetAttributes[EvaluateOnce, HoldAllComplete]
EvaluateOnce[exprs:PatternSequence[_, __]] :=
Replace[Hold @@ Evaluate /@ EvaluateOnce /@ Hold[exprs], Hold[e_] :> e, 1]
EvaluateOnce[expr_] :=
Module[{depth = 0, length = [email protected]@expr, tag, enter, exit}
, SetAttributes[exit, HoldAllComplete]
; enter[in_]:= If[1 === depth && 0 === length, Throw[in, tag], ++depth]
; exit[in_, out_] := (If[2 === depth, length--]; depth--)
; Hold @@ Catch[With[{r = TraceScan[enter, expr, _, exit]}, Hold[r]], tag]
]
Эта функция предоставляется без гарантии:) Она использует TraceScan
и некоторые эвристики, чтобы догадаться, когда "единый уровень оценки" завершен, а затем использует Throw
и Catch
для раннего завершения оценки.
Эвристика, по-видимому, удовлетворительно работает для определений функций, чей "первый уровень оценки" остается в пределах стандартной оценки. Он также терпит неудачу для тех, кто этого не делает. Я также уверен, что это запутается с применением некоторых оценочных атрибутов.
Несмотря на эти ошибки, я по-прежнему считаю эту функцию удобной при попытке отладки или даже просто понимать функции с множеством стандартных определений соответствия шаблонов.
Ответ 3
Вы можете попробовать использовать ToExpression
:
In[9]:= list = {"f1", "f2"};
In[19]:= f1 = 25;
In[20]:= ToExpression[
StringJoin["{", Riffle[list, ","], "}"], InputForm,
Function[vars, Block[vars, f1], HoldAll]]
Out[20]= 25
Ответ 4
Вы можете рассмотреть эту конструкцию:
SetAttributes[block, HoldRest]
block[s : {__String}, body_] :=
Function[, Block[{##}, body], HoldAll] @@
Join @@ MakeExpression /@ s
Вторая попытка более короткой версии второй функции Леонида:
block =
Function[, Block @@ [email protected]@#[email protected]#2, HoldRest]