How to make an analog of InString[]?

后端 未结 3 1744
独厮守ぢ
独厮守ぢ 2021-01-20 05:59

I have discovered that InString[] does not work in MathLink mode when sending input with EnterExpressionPacket header. So I need to de

相关标签:
3条回答
  • 2021-01-20 06:07

    I would use $Pre and $Line for this; unlike $PreRead, it's applied to input expressions, not input strings or box forms. All you need is to assign it a function that has the HoldAllComplete attribute, like this one which I've adapted from the example in the documentation:

    SetAttributes[saveinputs, HoldAllComplete];
    saveinputs[new_] :=
     With[{line = $Line},
      inputs[line] = HoldComplete[new]; new]
    $Pre = saveinputs;
    

    I tested this with MathLink, and the behavior seems to be what you desired (I've elided some of the transcript to highlight the key point):

    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]]}]
    
    0 讨论(0)
  • 2021-01-20 06:15

    I just have found simpler but dangerous way:

    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]]
    

    Does anybody know a way to make it safe?

    0 讨论(0)
  • 2021-01-20 06:27

    It seems that I have solved the problem. Here is the function:

    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]
    

    And I just have got the answer to the question on InString in MathLink mode from Todd Gayley (Wolfram Research):

    InString is only assigned when using EnterTextPacket, not EnterExpressionPacket. There is no string form of the input when sending EnterExpressionPacket (whose content is, by definition, already an expression).

    EDIT:

    I just have found that my code does not work with input expressions with head Evaluate. The solution is to replace HoldForm by HoldComplete in my code:

    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]]
    

    This works well. Another approach would be to unprotect HoldForm and set up attribute HoldAllComplete on it. I'm wondering why HoldForm does not have this attribute by default?

    EDIT 2:

    In the comments for the main question Leonid Shifrin suggested much better solution:

    getLastInput := 
     Block[{RuleDelayed},SetAttributes[RuleDelayed,HoldAllComplete];
      With[{line=$Line-1},HoldComplete[In[line]]/.DownValues[In]]]
    

    See comments for details.

    EDIT 3: The last code can be made even better for by replacing HoldComplete by double HoldForm:

    getLastInput := 
     Block[{RuleDelayed},SetAttributes[RuleDelayed,HoldAllComplete];
      With[{line=$Line-1},HoldForm@HoldForm[In[line]]/.DownValues[In]]]
    

    The idea is taken from presentation by Robby Villegas of Wolfram Research at the 1999 Developer Conference. See subsection "HoldCompleteForm: a non-printing variant of HoldComplete (just as HoldForm is to Hold)" in "Working With Unevaluated Expressions" notebook posted here.

    0 讨论(0)
提交回复
热议问题