Как заблокировать символы, не оценивая их?

Предположим, у меня есть список имен 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]