統計解析R たぶんpart3くらい (587レス)
前次1-
抽出解除 必死チェッカー(本家) (べ) 自ID レス栞 あぼーん

リロード規制です。10分ほどで解除するので、他のブラウザへ避難してください。
471: 2023/02/26(日)00:14 ID:Qcp1BikG(1/6) AAS
上のコード`uncertainly`の挙動を"プロミスの罠"と書くことにする。
ここでの"プロミス"は、JSの"プロミス"ではなくて、Rでの"変数"の
実装方法を指す。

* [6 Functions | Advanced R](外部リンク:adv-r.hadley.nz

この記事には次の一節がある。

> You cannot manipulate promises with R code. Promises are like a **quantum state:**: ...

多分、ここでの"量子状態"はハイゼンバグと同じ現象を指しているんだと思う。
観測すると、状態が変化してしまう。

関数`do.call`自体はプロミスの罠と関係しないと思う。

``` {r a_28697, dependson = ""}
do.call (`+`, list (1L, 2L))
```
Pythonだと次のコードに対応する。

``` {python a_10786, dependson = ""}
(lambda x, y: x + y) (* range (1, 3))
```
殆どのプログラミング言語で、関数の引数リストは
[一級市民](外部リンク:en.wikipedia.org
でないように思う。一級市民でない代わりに、一級市民の配列からの変換が
用意されている。Pythonでは`*`という関数がビルトインで用意されている。
Rでは引数リストを直接作れるかもしれないが、`do.call`で配列から
変換するのが一般的だと思う。
472: 2023/02/26(日)23:03 ID:Qcp1BikG(2/6) AAS
書けるかな?
473: 2023/02/26(日)23:09 ID:Qcp1BikG(3/6) AAS
自分の知る限り、プロミスの罠にハマるのは次のパターンに限られる。

~~~ {.r}
x = "hello"
g = f (x)
x = "world"
g ()
~~~
"関数を返す関数"`f`に、"変数"`x`を代入した時にプロミスの罠が可能性が
出てくる。Rの評価戦略はHaskellと同じ
[コールバイニード](外部リンク:en.wikipedia.org
に分類されている。上の例では、次の場合にプロミスの罠が発生する。

1. 関数`f (x)`の中で引数`x`がニードにならず、
1. 返り値の関数`g`に渡される。

次の例はプロミスの罠が発生する。

``` {r a_20914, dependson = ""}
rude = with (new.env (), {
const = \(x) \(...) x
x = "hello"
g = const (identity (x))
x = "world"
g () |> print ()
});
```
関数への代入はニードでないので(by definition)、関数適用の連鎖
`const (identity (x))`の中にはニードがない。そのために、最終的に`g ()`が
コールされるまで引数`x`は評価されない。コード`g ()`がコールされた時点で
引数`x`の定義を探しに行くので、プロミスの罠にハマる。
474: 2023/02/26(日)23:11 ID:Qcp1BikG(4/6) AAS
コールバイニードを実現するためには、コンパイラーが内部的に次のように
書き換える必要があると思う。

``` {r a_1451, dependson = ""}
polite = with (new.env (), {
const = \(x) \(...) x
x_1 = "hello"
g = const (identity (x_1))
x_2 = "world"
g () |> print ()
});
```
[静的単一代入](外部リンク:en.wikipedia.org
と呼ばれる操作と同じだと思う。この書き換えはループ処理では必須になる。
コード`rude`はアカン奴として切り捨てることができるかもしれないが、
ループ処理では変数の多重定義が避けられない。Rでは、何らかの理由で、
この書き換えが省略されているために、プロミスの罠が発生するのかな?と
思っている。

コード`rude`からコード`polite`への書き換えを上とは異なる形で行ってみる。
Rではできないので、Pythonを使う。
475: 2023/02/26(日)23:12 ID:Qcp1BikG(5/6) AAS
``` {python a_14213, dependson = ""}
##| cache: false
#
def none ():
id = lambda a: a
const = lambda x: lambda * y: x
def lhs ():
x = "hello"
g = const (id (x))
x = "world"
return g ()
def rhs ():
return (lambda x: (lambda g: (lambda x: g ()) ("world")) (const (id (x)))) ("hello")
assert lhs () == rhs ()
none ();
```
関数`lhs`から関数`rhs`への書き換え規則は次のようになる。[rule]{#rule}

~~~ {.python}
y = f (x)
z = g (x, y)
rest (x, y, z)
==
(lambda y:
z = g (x, y)
rest (x, y, z)
) (f (x))
==
(lambda y: (lambda z: rest (x, y, z)) (g (x, y))) (f (x))
~~~
476: 2023/02/26(日)23:25 ID:Qcp1BikG(6/6) AAS
この書き換え規則を適用した結果、関数`rhs`では、静的単一代入への変換は、
[ラムダ計算](外部リンク:en.wikipedia.orgのアルファ変換
に置き換わる。この意味では、静的単一代入とアルファ変換は同じことになる。
実際のコードでは、分岐、ループ、副作用が入ってくるので、こんな単純は
話では済まないと思うが、極度に単純化すると、静的単一代入とアルファ変換は、
単に、方言の違いということになる。

Rの場合は、書き換え規則[rule](#rule)は等価な変換にならない可能性がある。
特に、プロミスの罠が現れた場合は、挙動が変わると思う。

余談だが、書き換え規則[rule](#rule)をモナドに拡張したものがHaskellの
[do-記法](外部リンク:en.wikibooks.orgになっている。

~~~ {.python}
y <- f (x)
z <- g (x, y)
rest (x, y, z)
==
kleisli-extension (lambda y:
z <- g (x, y)
rest (x, y, z)
) (f (x))
~~~
モナドが恒等関数の場合に[rule](#rule)に一致するので、do-記法を
[rule](#rule)の拡張と言って差し支えないと思う。

おしまい
前次1-
スレ情報 赤レス抽出 画像レス抽出 歴の未読スレ AAサムネイル

ぬこの手 ぬこTOP 0.031s