Mathematicaで内部処理を隠蔽する

Mathematicaのシンボルにはいくつかの属性を設定でき、ReadProtected属性を付与することで定義を隠蔽することができるが、 DownValuesUpValuesを使うことで定義を表示することができる他、TraceTracePrintを使うことで内部処理を追跡することが可能である。

そこで、ReadProtected属性に加えてLocked属性を付与することで内部処理を隠蔽することができる。

通常

何も属性を付与しない場合、Information(?、??)DefinitionDownValuesUpValuesを使うことで定義を確認できる。

In[1]:= f[x_] := Sin[x] + 1;

In[2]:= Definition[f] // OutputForm

Out[2]//OutputForm= f[x_] := Sin[x] + 1

In[3]:= DownValues[f]

Out[3]= {HoldPattern[f[x_]] :> Sin[x] + 1}

もちろん、Traceで処理を追うことも可能である。

In[4]:= Trace[f[2]] // OutputForm

Out[4]//OutputForm= {f[2], Sin[2] + 1, 1 + Sin[2]}

ReadProtectedのみ

属性ReadProtectedを付与すると、??Definitionでは定義が表示されなくなる。

In[5]:= g[x_] := Sin[x] + 1;
SetAttributes[g, {ReadProtected}];

In[7]:= Definition[g] // OutputForm

Out[7]//OutputForm= Attributes[g] = {ReadProtected}

ただし、依然としてDownValuesでは定義を表示可能で、Traceで処理を追跡することも可能である。

In[8]:= DownValues[g]

Out[8]= {HoldPattern[g[x_]] :> Sin[x] + 1}

In[9]:= Trace[g[2]] // OutputForm

Out[9]//OutputForm= {g[2], Sin[2] + 1, 1 + Sin[2]}

Lockedのみ

属性Lockedをだけを付与しても、これ以上のシンボルの変更ができなくなるだけで、定義を参照することは通常と変わらない。

In[10]:= h[x_] := Sin[x] + 1;
SetAttributes[h, {Locked}];

In[12]:= Definition[h] // OutputForm

Out[12]//OutputForm= Attributes[h] = {Locked}
 
h[x_] := Sin[x] + 1

In[13]:= DownValues[h]

Out[13]= {HoldPattern[h[x_]] :> Sin[x] + x}

In[14]:= Trace[h[2]] // OutputForm

Out[14]//OutputForm= {h[2], Sin[2] + 1, 1 + Sin[2]}

ReadProtected + Locked

ReadProtectedLockedの両方を付与した場合に、DownValuesTraceを使っても定義を隠蔽することが可能になる。

In[15]:= i[x_] := Sin[x] + 1;
SetAttributes[i, {ReadProtected, Locked}];

Definitionを使っても属性以外は表示されない。

In[17]:= Definition[i] // OutputForm

Out[17]//OutputForm= Attributes[i] = {Locked, ReadProtected}

DownValuesでは「シンボルの読出しはできません」とメッセージが表示されて失敗する。

In[18]:= DownValues[i]

In[18]:= General::readp: シンボルiの読出しはできません. >>

Out[18]= $Failed

Traceを使っても、内部処理部分は表示されず、一気に表示される(TracePrintも同様)。

In[19]:= Trace[i[2]] // OutputForm

Out[19]//OutputForm= {i[2], 1 + Sin[2]}

回避方法

定義を行う前に大域変数$Pre$PreReadを設定することで、定義される瞬間にどのような式が評価されようとしているのかを見ることができてしまう。

In[20]:= $Pre = Function[a, Print[Hold[a]]; a, {HoldAll}]

In[21]:= j[x_] := Cos[x] + 1

Hold[j[x_]:=Cos[x]+1]

回避方法の回避方法

定義をファイルから読み込む場合は$Pre$PreReadを設定していた場合でも隠すことができる。

もちろんテキストエディタでファイルの中身を読まれては行けないので、Encodeで符号化しておく。

In[22]:= Export[FileNameJoin[{$TemporaryDirectory, "test.m"}], "k[x_]:=Sin[x]+x+1;SetAttributes[k,{ReadProtected,Locked}];", "Text"];

In[23]:= Encode[FileNameJoin[{$TemporaryDirectory, "test.m"}], FileNameJoin[{$TemporaryDirectory, "test.m.enc"}]];

In[24]:= {$Pre, $PreRead} = {#, #} &@ Function[a, Print[Hold[a]]; a, {HoldAll}];

In[25]:= Get[FileNameJoin[{$TemporaryDirectory, "test.m.enc"}]]

Hold[RowBox[{Get,[,RowBox[{FileNameJoin,[,RowBox[{{,RowBox[{$TemporaryDirectory,,,"test.m.enc"}],}}],]}],]}]]

Hold[Get[FileNameJoin[{$TemporaryDirectory,test.m.enc}]]]

結論

  • シンボルに属性ReadProtectedLockedの両方を設定することで、大体の場合の内部処理を隠蔽することが可能。
  • 定義をファイルから読み込むことで$Pre$PreReadを使って定義される瞬間も隠蔽可能。

参考