Mathematicaで内部処理を隠蔽する
Mathematicaのシンボルにはいくつかの属性を設定でき、ReadProtected属性を付与することで定義を隠蔽することができるが、
DownValuesやUpValuesを使うことで定義を表示することができる他、TraceやTracePrintを使うことで内部処理を追跡することが可能である。
そこで、ReadProtected属性に加えてLocked属性を付与することで内部処理を隠蔽することができる。
通常
何も属性を付与しない場合、Information(?、??)やDefinition、DownValues、UpValuesを使うことで定義を確認できる。
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
ReadProtectedとLockedの両方を付与した場合に、DownValuesやTraceを使っても定義を隠蔽することが可能になる。
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}]]]
結論
- シンボルに属性
ReadProtectedとLockedの両方を設定することで、大体の場合の内部処理を隠蔽することが可能。 - 定義をファイルから読み込むことで
$Preや$PreReadを使って定義される瞬間も隠蔽可能。