Conference Room SAD
[thread display] [new arrival display] [word search] [past log] [管理用]

Subject Re: Withの束縛の取扱いがおかしい
Date: 2012/06/11(Mon) 14:36:29
ContributorAkio 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の動作はこれを破壊してしまいます。


- 関連一覧ツリー (Click ▼ to display all articles in a thread.)