Как сделать аналог InString[]?
Я обнаружил, что InString[]
не работает в MathLink
режим при отправке ввода с EnterExpressionPacket
заголовок. Поэтому мне нужно определить свою собственную функцию, которая возвращает предыдущую строку ввода. Один способ, которым я разработал here
не работает в некоторых случаях:
In[1]:= Unevaluated[2 + 2]
With[{line = $Line - 1}, HoldForm[In[line]]] /. (DownValues[In])
Out[1]= Unevaluated[2 + 2]
Out[2]= 2 + 2
Это потому что RuleDelayed
не имеет HoldAllComplete
приписывать. Добавление этого атрибута делает это нормально:
In[1]:= Unprotect[RuleDelayed];
SetAttributes[RuleDelayed, HoldAllComplete];
Protect[RuleDelayed];
Unevaluated[2 + 2]
With[{line = $Line - 1}, HoldForm[In[line]]] /. DownValues[In]
Out[4]= Unevaluated[2 + 2]
Out[5]= Unevaluated[2 + 2]
Но изменение встроенных функций обычно не очень хорошая идея. Есть лучший способ сделать это?
3 ответа
Кажется, я решил проблему. Вот функция:
In[1]:=
getLastInput := Module[{num, f},
f = Function[{u, v},
{u /. {In -> num, HoldPattern -> First}, HoldForm[v]}, HoldAllComplete];
First@Cases[
Block[{RuleDelayed = f}, DownValues[In]],
{$Line - 1, x_} -> x, {1}, 1]]
In[2]:=
Unevaluated[2+2]
getLastInput
Out[2]=
Unevaluated[2+2]
Out[3]=
Unevaluated[2+2]
И я только что получил ответ на вопрос о InString
в MathLink
Режим от Тодда Гейли (Wolfram Research):
InString назначается только при использовании EnterTextPacket, а не EnterExpressionPacket. При отправке EnterExpressionPacket отсутствует строковая форма ввода (содержимое которой, по определению, уже является выражением).
РЕДАКТИРОВАТЬ:
Я только что обнаружил, что мой код не работает с входными выражениями с головой Evaluate
, Решение состоит в том, чтобы заменить HoldForm
от HoldComplete
в моем коде:
getLastInput := Module[{num, f},
f = Function[{u, v},
{u /. {In -> num, HoldPattern -> First}, HoldComplete[v]}, HoldAllComplete];
First@Cases[
Block[{RuleDelayed = f}, DownValues[In]],
{$Line - 1, x_} -> x, {1}, 1]]
Это хорошо работает. Другим подходом было бы снять защиту HoldForm
и настроить атрибут HoldAllComplete
в теме. Мне интересно почему HoldForm
не имеет этого атрибута по умолчанию?
РЕДАКТИРОВАТЬ 2:
В комментариях к основному вопросу Леонид Шифрин предложил гораздо лучшее решение:
getLastInput :=
Block[{RuleDelayed},SetAttributes[RuleDelayed,HoldAllComplete];
With[{line=$Line-1},HoldComplete[In[line]]/.DownValues[In]]]
Смотрите комментарии для деталей.
РЕДАКТИРОВАТЬ 3: последний код можно сделать еще лучше, заменив HoldComplete
двойным HoldForm
:
getLastInput :=
Block[{RuleDelayed},SetAttributes[RuleDelayed,HoldAllComplete];
With[{line=$Line-1},HoldForm@HoldForm[In[line]]/.DownValues[In]]]
Идея взята из презентации Робби Виллегаса из Wolfram Research на конференции разработчиков в 1999 году. См. Подраздел "HoldCompleteForm: непечатаемый вариант HoldComplete (так же, как удержание HoldForm)" в записной книжке "Работа с неоцененными выражениями", размещенной здесь.
Я хотел бы использовать $Pre
а также $Line
за это; В отличие от $PreRead
, он применяется к выражениям ввода, а не к строкам ввода или блочным формам. Все, что вам нужно, это назначить ему функцию, которая имеет HoldAllComplete
атрибут, как этот, который я адаптировал из примера в документации:
SetAttributes[saveinputs, HoldAllComplete];
saveinputs[new_] :=
With[{line = $Line},
inputs[line] = HoldComplete[new]; new]
$Pre = saveinputs;
Я проверил это с помощью MathLink, и поведение, кажется, соответствует желаемому (я исключил некоторые записи, чтобы выделить ключевой момент):
In[14]:= LinkWrite[link,
Unevaluated[
EnterExpressionPacket[
SetAttributes[saveinputs, HoldAllComplete];
saveinputs[new_] :=
With[{line = $Line},
inputs[line] = HoldComplete[new]; new];
$Pre = saveinputs;]]]
In[15]:= LinkRead[link]
Out[15]= InputNamePacket["In[2]:= "]
In[20]:= LinkWrite[link,
Unevaluated[EnterExpressionPacket[Evaluate[1 + 1]]]]
In[21]:= LinkRead[link]
Out[21]= OutputNamePacket["Out[2]= "]
In[21]:= LinkRead[link]
Out[21]= ReturnExpressionPacket[2]
In[24]:= LinkWrite[link, Unevaluated[EnterExpressionPacket[DownValues[inputs]]]]
In[26]:= LinkRead[link]
Out[26]= ReturnExpressionPacket[
{HoldPattern[inputs[2]] :> HoldComplete[Evaluate[1 + 1]],
HoldPattern[inputs[3]] :> HoldComplete[DownValues[inputs]]}]
Я просто нашел более простой, но опасный способ:
In[3]:= Unevaluated[2 + 2]
Trace[In[$Line - 1]] // Last
Trace[In[$Line - 1]] // Last
Out[3]= Unevaluated[2 + 2]
Out[4]= Unevaluated[2 + 2]
During evaluation of In[3]:= $RecursionLimit::reclim: Recursion depth of 256 exceeded. >>
During evaluation of In[3]:= $RecursionLimit::reclim: Recursion depth of 256 exceeded. >>
During evaluation of In[3]:= $IterationLimit::itlim: Iteration limit of 4096 exceeded. >>
Out[5]= Hold[In[$Line-1]]
Кто-нибудь знает способ сделать это безопасно?