Subject | : Re: Withの束縛の取扱いがおかしい |
Article No | : 1024 |
Date | : 2012/06/11(Mon) 14:36:29 |
Contributor | : Akio Morita |
> 同一シンボルに対して、Withで束縛する場合の動作が一貫していないように見えます > > 例題として > With[{val = foo}, > Scan[With[{val = #}, Print[ToString[val]]]&, {1, 2, val, 3, 4}]]; > を考えます。ここで、内側のWithで束縛される valは Scan[]に与えた純関数スコープで閉じています。 > > 期待される出力は、 > 1 > 2 > foo > 3 > 4 > となります > > ここで、fooに NaNを設定した式 > With[{val = NaN}, > Scan[With[{val = #}, Print[ToString[val]]]&, {1, 2, val, 3, 4}]]; > を評価すると出力は > NaN > NaN > NaN > NaN > NaN > となり、内側のWithによる valの再束縛に失敗しているように見えます > 内部解析の結果、これは Replace[]の影響と判明しました(*1)
Real型に対する比較の実装が、IEEE754の算術比較なので NaN同士が同値にならないため 次のようになります {1, NaN, 3}/.{1->2} -> {2, NaN, 3} {1 ,NaN, 3}/.{NaN->2} -> {1, NaN, 3}
したがって、With束縛にReplaceの内部実装を流用しているので、上記の Withの問題は With[{val = NaN}, Scan[With[{val = #}, Print[ToString[val]]]&, {1, 2, val, 3, 4}]]; -> Scan[With[{NaN = #}, Print[ToString[NaN]]]&, {1, 2, NaN, 3, 4}]; と変換されたあとに、局所シンボル NaNの置換が失敗していることになります
(*1)に対するパッチは開発済みで、上記の問題は解決するのですが、With束縛自身に次の問題があることが分かりました
次のコードの振る舞いを考えてみてください f = With[{val = #}, Print[ToString[1 + val]]]&; With[{val = 1}, Scan[f, {1, 2, val, 3, 4}]; ]; 上記コード中の シンボル fに束縛される純関数を即値形式にした式 With[{val = 1}, Scan[With[{val = #}, Print[ToString[1 + val]]]&, {1, 2, val, 3, 4}]; ]; は動作が変わってしまいます 同様に、 f = With[{val = #}, Print[ToString[1 + val]]]&; With[{f, val = 1}, Scan[f, {1, 2, val, 3, 4}]; ]; と f = With[{val = #}, Print[ToString[1 + val]]]&; With[{f}, With[{val = 1}, Scan[f, {1, 2, val, 3, 4}]; ]]; でも動作が変わります
Withによつ局所シンボル束縛が、内側のWith局所シンボルスコープまで作用していることが事の本質のようですが、 これは意図された仕様なのでしょうか?
純関数の中でWithを使う意図としては、シンボルを局所拘束して外部との干渉を避けることが目的ですが、 現行のWithの動作はこれを破壊してしまいます。
|