Closures vs Objects
1 :デフォルトの名無しさん :2024/02/19(月) 20:39:15.91 ID:VT95BnI9.net It is well-known that closures and objects are equivalent. So are actors. Which one is the most fundamental? よく知られたように、クロージャとオブジェクトは等価です。アクターも同様です。どの概念が最も基本的なのでしょうか?
2 :デフォルトの名無しさん :2024/02/19(月) 20:49:06.93 ID:VT95BnI9.net A closure is a function with state in the local scope. An object also has methods and state which belong to its instance. These are compatible with each other. クロージャはローカル・スコープに状態をもつ関数です。 オブジェクトもまたそのインスタンスに属するメソッドと状態を持ちます。 これらは互いに互換性があります。
3 :デフォルトの名無しさん :2024/02/20(火) 09:36:12.34 ID:fQy0X2aE.net オブジェクトは複数のメソッドを持てる クロージャは関数をひとつしか返せない オブジェクトの勝ち
4 :デフォルトの名無しさん :2024/02/20(火) 11:01:18.21 ID:BFMbzAij.net >>3 ハッシュテーブルに関数を追加して返すことで、複数のメソッドを返すことが可能 より柔軟に、オブジェクトとそれを操作する関数の組を返すこともできるし、無限個のメソッドをもったオブジェクトだって作れる これらは書く対象によって使い分ければよいのであって、オブジェクトにメソッドが属しているのが常に最適とは限らない たとえばイベントハンドラにコールバック関数として渡すような場合は、ひとつの関数として取得できたほうが便利
5 :デフォルトの名無しさん :2024/02/20(火) 23:29:16.13 ID:0cXn/136.net クロージャのほうがprimitive 今時、高階関数やイテレータを書くのにわざわざデザインパターンとか使いたいかって話 クラスは、レキシカル環境つきオブジェクトだけが欲しくても必ず新しい名前空間が作られるし、単品で購入できない福袋みたいなもん
6 :デフォルトの名無しさん :2024/02/21(水) 05:55:14.17 ID:j79lZ4PO.net 二変数以上の関数のほとんどは、どの引数のオブジェクトに属しているともアプリオリには言えないし、オブジェクト指向は不自然 オブジェクトと引数は別物だし、もし関数を特定のオブジェクトに属するようにしたいなら、クロージャでラップすれば簡単にできる
7 :デフォルトの名無しさん :2024/02/21(水) 05:56:37.93 ID:j79lZ4PO.net 二変数以上の関数のほとんどは、どの引数のオブジェクトに属しているともアプリオリには言えないし、オブジェクト指向は不自然 オブジェクトと関数は別物だし、もし関数を特定のオブジェクトに属するようにしたいなら、クロージャでラップすれば簡単にできる
8 :デフォルトの名無しさん :2024/02/21(水) 10:09:33.92 ID:HBySQ9lj.net 2変数関数をクロージャにラップして1変数関数にしても、メソッドチェーンはできないよね
9 :デフォルトの名無しさん :2024/02/21(水) 10:29:18.43 ID:Cg6r5DK9.net [1, 2, 3, 4, 5]にメソッドが属してれば [1, 2, 3, 4, 5] .filter(hoge) # ←戻り値もオブジェクト .map(piyo) # ←これも .sum() みたいにできるけど、配列がオブジェクトじゃなかったら map([1, 2, 3, 4, 5], hoge) の戻り値はただの配列なので、繰り返し関数適用するには、Lispみたいな醜い入れ子にするしかない sum( map( filter([1, 2, 3, 4, 5], hoge), piyo))
10 :デフォルトの名無しさん :2024/02/21(水) 12:32:59.23 ID:pWazjr0Z.net >>8-9 これでどうよ chain = (obj) => { return (f, ...args) => { if (!f) return obj return chain(f(obj, ...args)) } } chain([1, 2, 3, 4, 5])( filter, (x) => x % 2)( map, (x) =>x * x)( sum)() // => 35
11 :デフォルトの名無しさん :2024/02/21(水) 15:43:45.17 ID:im/wtuEl.net 一時期メタプログラミングとかいって、動的にクラスやメソッドを作れるのがすごいって言われてたけど、クロージャをデータ構造として使うならそれは自明にできることでは
12 :デフォルトの名無しさん :2024/02/21(水) 16:28:56.20 ID:4iB0jwLo.net やり方が複数ある場合は、一番綺麗に書けるコードを採用するのが基本
13 :デフォルトの名無しさん :2024/02/21(水) 22:54:29.50 ID:DX/jvS2m.net 入れ子にしなくても関数合成でいいのでは? メソッドチェーンっぽく書くには Reverse-application operatorってのもあるけど 個人的には関数合成のほうがスッキリしててすこ https://ideone.com/gCWxLP let hoge n = n mod 2 = 1 let piyo = ( * ) 2 let () = print_int (List.fold_left (+) 0 (List.map piyo (List.filter hoge [1;2;3;4;5]))) (* function composition *) let (<<) f g x = f (g x) let f = print_int << List.fold_left (+) 0 << List.map piyo << List.filter hoge let () = f [1;2;3;4;5] let (>>) f g x = g (f x) let f = List.filter hoge >> List.map piyo >> List.fold_left (+) 0 >> print_int let () = f [1;2;3;4;5] (* @@ Application operator *) let () = print_int @@ List.fold_left (+) 0 @@ List.map piyo @@ List.filter hoge [1;2;3;4;5] (* |> Reverse-application operator *) let () = [1;2;3;4;5] |> List.filter hoge |> List.map piyo |> List.fold_left (+) 0 |> print_int
14 :デフォルトの名無しさん :2024/02/22(木) 01:30:07.21 ID:seCGgTJE.net Forth で書いてよ
15 :デフォルトの名無しさん :2024/02/22(木) 08:01:15.94 ID:5dknMZXg.net >>13 カリー化のなせる技やね
16 :デフォルトの名無しさん :2024/02/22(木) 08:40:34.15 ID:GWomKHpC.net chainl = (obj) => { return (f, arg) => { if (!f) return obj return chainl(f(arg, obj)) } } cons = (x, xs) => [x, xs] foreach = (lst, f) => { if(!lst) return [x, xs] = lst f(x) foreach(f, xs) } linkedList = chainl(null)( cons, 1)( cons, 2)( cons, 3)() chainl(linkedList)(foreach, console.log)() // 3 // 2 // 1 いろいろ書けるんだな
17 :デフォルトの名無しさん :2024/02/22(木) 10:55:42.37 ID:2PGen9gt.net カリー化もクロージャと可変長引数があればできるね currying = (f, ...args) => (...rest) => f(...args, ...rest)
18 :デフォルトの名無しさん :2024/02/22(木) 11:04:45.60 ID:2PGen9gt.net これで、mapなどが map(f, col) と、関数を先にとる形でも、>>16 のchainlを用いて chainl([1, 2, 3, 4, 5])( currying(filter, (x) => x % 2))( currying(map, (x) => x * x))( sum)() と書けるわけか
19 :デフォルトの名無しさん :2024/02/22(木) 11:11:29.91 ID:2PGen9gt.net いや、単にchainlを chainl = (obj) => { return (f, ...arg) => { if (!f) return obj return chainl(f(...arg, obj)) } } とすればいいのか
20 :デフォルトの名無しさん :2024/02/22(木) 12:04:47.68 ID:0KLGqPZR.net クロージャの真価はコンテキストを導入できること (let ((a 1) (b 2)) (+ a b)) ((lambda (a b) (+ a b)) 1 2) は等価 なので、上のa + bの変わりに関数を返却すれば独立したスコープの状態をもつ何かを作れる クラスベースのオブジェクト指向でも同様だが、そのようなコンテキストのテンプレート(= class)は静的にしか作れない
21 :デフォルトの名無しさん :2024/02/22(木) 12:15:29.98 ID:Q3haYGVO.net 静的なほうがバグの発見は早そう
22 :デフォルトの名無しさん :2024/02/22(木) 13:03:51.54 ID:BdBf3uc1.net なんでも自由を許すと無秩序になる >>10 だって、シグネチャを無視することが前提の設計になってる (最後に結果を呼び出す時は第一引数のfを省略する) TypeScriptが使われるのもそれが理由 今やPythonやRubyにも型がある
23 :デフォルトの名無しさん :2024/02/22(木) 13:23:57.40 ID:v9QmWW2c.net くだらん https://stackoverflow.com/questions/2497801/closures-are-poor-mans-objects-and-vice-versa-what-does-this-mean
24 :デフォルトの名無しさん :2024/02/22(木) 13:24:08.05 ID:WrtsfcL/.net 型なんて、プログラマがちまちま書かずとも、コンパイラや型チェッカが実行前に検査してくれればいいと思う
25 :デフォルトの名無しさん :2024/02/22(木) 15:02:53.21 ID:bbu+R1aP.net currying = (f) => { args = [] apply = (a) => { if (a === undefined) return f(...args) args.push(a) return apply } return apply }
26 :デフォルトの名無しさん :2024/02/22(木) 18:39:34.82 ID:SfbO+Gz+.net クロージャはポリモーフィズムができない オブジェクト指向言語ではたとえば比較演算子の動作を型によって変えられる a.compare(b)とソースコードのどこに書いても、aの型に応じて適切にcompareの動きが選択される 変数の型によって実行する関数を変えるにはどうする? まさか関数定義に型による条件分岐をいちいち書くわけじゃあるまいし
27 :デフォルトの名無しさん :2024/02/22(木) 19:07:57.79 ID:v9QmWW2c.net class構文が入るまでどうやってJSでクラスを定義してたのか調べれば分かるよ
28 :デフォルトの名無しさん :2024/02/22(木) 19:20:20.77 ID:bu5i+qrs.net >>27 a.hoge()のようにしてダックタイピングするなら当然できる hogeがa(or a.prototype)に属さないただの関数の場合でも hogeの中で型による条件分岐を書く以外の方法でできるか
29 :デフォルトの名無しさん :2024/02/22(木) 19:30:27.02 ID:coyprm7i.net https://www.yunabe.jp/docs/javascript_class_in_google.html
30 :デフォルトの名無しさん :2024/02/22(木) 19:41:55.54 ID:v9QmWW2c.net >>28 なんて?
31 :デフォルトの名無しさん :2024/02/22(木) 20:46:51.05 ID:5LGf2QVl.net >>26 ,28 a, bではなくcompareのほうを、状態をもった関数オブジェクトとして生成する 一例として、 compareのクロージャには、「型名=>その型の引数を受け取った時に呼び出す関数」のテーブルを保持する そのテーブルに関数を動的に登録するための関数を作成する 例: IComparable = () => { dispatch = {} compare = (self, other) => { /*dispatchテーブルから関数を検索して実行*/ } implement = (type, fn) => { /* dispatchテーブルにfnを登録 */ } return [compare, implement] } もちろんdispatchの条件は型名じゃなくても、別の判定用関数を用意してもいい
32 :デフォルトの名無しさん :2024/02/22(木) 20:49:14.36 ID:5LGf2QVl.net インタフェースの実装は、型定義に書くよりも使用時に動的にできた方が自然だ たとえば木構造にイテレータを実装するとして、深さ優先探索にするのか幅優先探索にするのかは、その時々によって変わる。木構造の定義だけからは決まらない
33 :デフォルトの名無しさん :2024/02/22(木) 22:14:42.54 ID:BzyFji/L.net 最近のフレームワークはどんどん脱オブジェクト指向・関数型化してないか? Pythonのフレームワークは機能のMixinは継承ではなくデコレータ(ようはクロージャ)によるものが多い Reactなんか思想は元々関数型的だったけど、ついにクラスコンポーネントやめちゃった
34 :デフォルトの名無しさん :2024/02/23(金) 00:14:17.41 ID:vwgp24xa.net cps = (f) => (...args) => args.pop()(f(...args)) eq = cps((a, b) => a === b) sub = cps((a, b) => a - b) mul = cps((a, b) => a * b) fact = (n, k) => { eq(n, 0, (isZero) => { if(isZero) { return k(1) } else { return sub(n, 1, (prevn) =>{ fact(prevn, (prevf) => { mul(n, prevf, k) }) }) } }) } fact(5, console.log)
35 :デフォルトの名無しさん :2024/02/23(金) 13:52:03.15 ID:P8ZW/vvm.net Closures are functions with contexts. Objects are structures with contexts. A structure can have functions as its member, and vice versa. So both are equal.
36 :デフォルトの名無しさん :2024/02/23(金) 19:20:29.47 ID:mZKLNS+O.net オブジェクトよりも、クロージャよりも、第一級継続のほうが強い
37 :デフォルトの名無しさん :2024/02/24(土) 00:22:58.31 ID:PF21lJOY.net >>36 パフォーマンスは?
38 :デフォルトの名無しさん :2024/02/24(土) 06:28:09.62 ID:dndVDS6q.net >>37 1. ハードウェア/コンパイラの技術向上に期待する 2. ネックになる部分だけ最適化する 3. そもそもパフォーマンスが必要ならC++やRustなどで書く
39 :デフォルトの名無しさん :2024/02/24(土) 11:30:44.99 ID:PF21lJOY.net >>38 その回答、第一級継続関係無いやん。
40 :デフォルトの名無しさん :2024/02/24(土) 21:14:52.39 ID:4BGFiybC.net オブジェクトとクロージャが使えることは、コードブロック(+ ローカル変数)を第一級オブジェクトに持っているのだと思える 継続が第一級オブジェクトなのは、プログラム実行時の任意の状態が第一級オブジェクトということ
41 :デフォルトの名無しさん :2024/02/24(土) 21:42:30.15 ID:fbMBQzh4.net >>40 継続使う場合のパフォーマンスは?
42 :デフォルトの名無しさん :2024/02/25(日) 00:23:07.77 ID:bNSutqDu.net >>41 昨日からどうした なにか嫌なことでもあったか?
43 :デフォルトの名無しさん :2024/02/25(日) 09:10:18.70 ID:zl+9FS4I.net 継続はクロージャで表現されるけど、オブジェクトとクロージャが同値なら、オブジェクトによる表現もできるの?
44 :デフォルトの名無しさん :2024/02/25(日) 10:52:31.35 ID:5QMstwYR.net : fact dup 1 = if drop else dup 1- rot rot * swap recurse then ; 1 5 fact cr .
45 :デフォルトの名無しさん :2024/02/25(日) 11:03:04.14 ID:O8qt4uJC.net インタフェースが静的に決まれば保守性はおちないと思う
46 :デフォルトの名無しさん :2024/02/25(日) 11:17:17.62 ID:azDE+Je3.net 近年まれにみる良スレ
47 :デフォルトの名無しさん :2024/02/25(日) 11:28:28.39 ID:PRxvM5Hr.net >>31 これ継承の問題点を全部解消してる上に継承よりも強力だな
48 :デフォルトの名無しさん :2024/02/25(日) 16:27:53.33 ID:4fScDWQR.net ハァ?
49 :デフォルトの名無しさん :2024/02/25(日) 16:36:21.69 ID:wVCQJTWx.net >>47 使いにくい上に動的ディスパッチとなっている 継承の問題点を解消している好例はRust その上で使いやすく静的ディスパッチによる単相化も可能で速い
50 :デフォルトの名無しさん :2024/02/25(日) 17:38:12.08 ID:EvE7ghxV.net >>49 どのへんが使いにくいのかがわからないし、動的ディスパッチだと何がよくないのかもわからない Rustのほうが早いのはそりゃ当たり前
51 :デフォルトの名無しさん :2024/02/25(日) 17:55:13.30 ID:BE8i65fG.net >>43 できるだろうけど、継続自体が手続き/関数的な概念なので、オブジェクトによる表現をするメリットは少ないと思う
52 :デフォルトの名無しさん :2024/02/25(日) 19:51:34.20 ID:O9l+7I5E.net 継続とコルーチンなら、継続のほうがprimitiveなの?
53 :デフォルトの名無しさん :2024/02/25(日) 20:50:19.80 ID:ZY2lsWgE.net >>52 継続が第一級なら保存した継続を何度も呼び出せるが、コルーチンはyieldしたらもうその時点には戻れないので、継続のほうが根本的
54 :デフォルトの名無しさん :2024/02/26(月) 12:34:53.26 ID:9feDQZG5.net なんでもクロージャだと戻り値全部関数だけど、型情報を持つことはできる? ディスパッチするのに必要だよね?
55 :デフォルトの名無しさん :2024/02/26(月) 15:19:09.55 ID:L4EYeETV.net >>54 type属性をもったオブジェクトとして返却すればいいんじゃないんですかね それを毎回書きたくないなら、生成用関数を受け取って新しい生成用関数を返す関数 makeConstructor = (f) => (type, ...args) => {type: type, value: f(...args)} みたいなのを使ってみてはどうでしょうか
56 :デフォルトの名無しさん :2024/02/26(月) 18:51:01.76 ID:k7j7cG9/.net >>55 これ、オブジェクトからtype属性つきオブジェクトへの関数、を持ち上げる関数書けばモナドになるから自動的に変換できるんだな
57 :デフォルトの名無しさん :2024/02/26(月) 22:39:42.49 ID:qQrR5NbL.net そもそもオブジェクトの型を動的に判定することは不可能なのだ ただのシンボル'0'が整数であるとは数学的に決して言えない 整数の性質を満たす集合に属しているから整数と言えるのだ だからオブジェクトが型を授かるためにクラスは絶対に必要なのだ
58 :デフォルトの名無しさん :2024/02/26(月) 22:55:13.16 ID:9WvTd710.net そもそもふだんそんなにポリモーフィズムしてるか? 型ヒント書いたら実行前にチェックできるとか、中間コードにコンパイルされる時高速になるとか、それでよくないか?
59 :デフォルトの名無しさん :2024/02/26(月) 22:58:38.34 ID:sdXywFcL.net モダンなプログラミング言語 Go、Rust、Zig、Nim、Julia、Elixirなどは クラスおよびその継承を言語仕様から排除している それぞれの言語は全く異なる方針を採っているがクラス排除だけは全てで一致している クラスとその継承は悪手であるとプログラミング言語界では結論が出ている
60 :デフォルトの名無しさん :2024/02/26(月) 23:36:19.27 ID:L97i44S4.net xの型がT y = Foo(x)なら yの型はFoo(T) というシステムでいいと思うの Length ::= Float Angle ::= Float Point ::= Tuple(Float, Float) Triangle ::= Triangle(Length, Length, Length) | Triangle(Length, Length, Angle) | Triangle(Length, Angle, Angle) | Triangle(Point, Point)
61 :デフォルトの名無しさん :2024/02/26(月) 23:50:16.48 ID:TyGRqLb7.net >>57 前半はそのとおり だがクラスは不要 変数の出自が辿れればいい
62 :デフォルトの名無しさん :2024/02/27(火) 00:02:52.99 ID:woInknqq.net クラスは不要で型クラスは必要
63 :デフォルトの名無しさん :2024/02/27(火) 06:56:00.52 ID:cCaBYlch.net Natural = fn [0] 0 [n: n >= 0] n end succ = fn [Natural(n)] Natural(n + 1) end add = fn [n, Natural(0)] n [n, succ(m)] succ(add(n, m)) end mul = fn [n, Natural(0)] Natural(0) [n, succ(m)] add(mul(n, m), n) end AdditiveMonoid = Monoid() AdditiveMonoid.implimentsIdentity(Natural, fn [] Natural(0) end) AdditiveMonoid.implimentsAppend(Natural, add) MultiplicativeMonoid = Monoid() MultiplicativeMonoid.implimentsIdentity(Natural, fn [] Natural(1) end) MultiplicativeMonoid.implimentsAppend(Natural, mul)
64 :デフォルトの名無しさん :2024/02/27(火) 11:48:35.86 ID:T+tKpG5+.net たしかに「この関数から返ってたらこの型」というのはそうか インタフェース型を返す関数から返った値は、その実体がなんであってもインタフェース型として受け取らないといけないのか
65 :デフォルトの名無しさん :2024/02/27(火) 14:07:25.42 ID:HfXhKUnc.net Pythonの型ヒントも、内部的にやってることは型の階層構造を保存したオブジェクト作ったりしてるだけだし、型をクロージャで表すというのは十分に現実的
66 :デフォルトの名無しさん :2024/02/28(水) 09:15:01.79 ID:ZVd213Rl.net なんでもクロージャでは構造に基づくサブタイピングができないから、ポリモーフィズムするなら必然的に名前に基づくサブタイピングをする必要がある
67 :デフォルトの名無しさん :2024/02/28(水) 18:59:36.77 ID:FE9gquBK.net 型注釈はクロージャで実現できるけど、肝心の個別のオブジェクトに型を付与するには、処理系に手加えないと無理だね
68 :デフォルトの名無しさん :2024/02/28(水) 20:20:50.87 ID:YGaU0GOk.net ただのクロージャにこれはLinkedListだみたいな型情報をつけるなら JavaScriptのようにすべてのオブジェクトがフィールドを持てるようにして、クロージャもtype属性みたいなのを持てるようにする Pythonのように__call__()メソッドを実装すればオブジェクトも関数として呼び出せるようにして、オブジェクトにラップする おとなしくにハッシュテーブルにラップするとかして、関数としてのインタフェースを保つのは諦める
69 :デフォルトの名無しさん :2024/02/28(水) 22:56:39.74 ID:E1vk9WfJ.net 型定義 Counter = Type(fn [n] fn [i] n += i end end) f = Counter(0) f is Counter # true f(1) # 1 f(2) # 3 f(3) # 6 エイリアス Adder = Counter f is Adder # true 多相型 Pair = Type(fn [T, S] fn [t: T, s: S] (t, s) end) p = Pair(Int, Str)(1, 'Hello') p is Pair(Int, Str) # true
70 :デフォルトの名無しさん :2024/02/28(水) 23:27:14.87 ID:BtUR2apE.net クロージャはその外の変数をキャプチャできてこそ本領を発揮する
71 :デフォルトの名無しさん :2024/02/29(木) 09:14:52.93 ID:kNys7UfZ.net 実行のたびに無名関数を生成すると Pair(Int, Str)の実体が毎回変わってしまう 同じ型として扱うならキャッシュしとかんといかん Foo()の結果がFoo型になるので、自然にヒエラルキー Pair(Int, Str) ⊂ Pair ⊂ Type ができる 子はすべての親を知っているが、親は直接の子しか知らない
72 :デフォルトの名無しさん :2024/02/29(木) 21:57:07.71 ID:FO1fpwyp.net ユニオン型 Male = Type() Female = Type() Sex = Male | Female 代数的データ型 Nil = Type() Cons = Type(fn [T] fn [x: T, xs: List(T)] (x, xs) end end) List = Type(fn [T] Nil | Cons(T) end) c = Cons(Int)(1, Nil()) c is Cons(Int) # true nl = Nil() nl is Nil # true nl is List(Int) # ( = Nil | Cons(T) ) true l = Cons(2, c) l is List(Int) # true
73 :デフォルトの名無しさん :2024/03/01(金) 09:17:33.48 ID:uT9YQz7T.net クロージャは (1) 評価可能な任意のコードブロックをオブジェクト化できる (2) 新しいコンテクストを導入できる (3) 評価を遅延可能 (4) 即時生成可能
74 :デフォルトの名無しさん :2024/03/01(金) 13:47:12.10 ID:hUzmZgl8.net クロージャは、評価可能な任意のコードブロックを第一級オブジェクトとして扱える 継続は、プログラム実行時の任意の状態と処理を第一級オブジェクトとして扱える
75 :デフォルトの名無しさん :2024/03/01(金) 15:52:11.47 ID:5dJkdiP3.net 欠点は何?
76 :デフォルトの名無しさん :2024/03/01(金) 16:27:28.29 ID:lcFJqP/H.net >>75 汎用的な制御構造として使うには難解すぎること
77 :デフォルトの名無しさん :2024/03/01(金) 17:06:01.87 ID:lcFJqP/H.net 継続を入れ子にするともう予測不可能になる
78 :デフォルトの名無しさん :2024/03/02(土) 00:04:51.39 ID:WOl8O7ii.net 言語の高速化は処理系の仕事なのだから、特定の処理系で実行速度を最適化するために冗長な書き方を強いられるのは馬鹿げている
79 :デフォルトの名無しさん :2024/03/02(土) 00:15:28.02 ID:A93OEkS3.net 型がクロージャなら例外もクロージャで表現できそう
80 :デフォルトの名無しさん :2024/03/02(土) 01:38:33.15 ID:vVlrIC3U.net >>76 そこはシンタックスシュガー用意できる?
81 :デフォルトの名無しさん :2024/03/02(土) 02:47:41.13 ID:WOl8O7ii.net >>80 たとえばコルーチンとか、非決定的なバックトラックとかなら
82 :デフォルトの名無しさん :2024/03/02(土) 09:23:44.53 ID:ykoZd5Kv.net >>79 例外処理こそ継続とパターンマッチを使うべきだよなあと思う
83 :デフォルトの名無しさん :2024/03/02(土) 11:12:48.28 ID:hFGO4sAk.net 決定性のバックトラックなんてあるの?
84 :デフォルトの名無しさん :2024/03/02(土) 16:06:45.66 ID:BdR3F6bT.net >>81 Pythonでいうとこのジェネレータ内包表記みたいなのがあれば、多くの場面で十分なのかな
85 :デフォルトの名無しさん :2024/03/02(土) 19:52:13.99 ID:IQAkxugo.net >>84 コルーチンの中でコルーチンを呼び出したら、yieldしたら最上位の呼び出し元まで返ってくるのが望ましい
86 :デフォルトの名無しさん :2024/03/02(土) 21:01:52.57 ID:yMdn9rGJ.net >>85 非同期コルーチンAの中で非同期コルーチンBを呼ぼうとしたら自動的にAはyieldして戻ってからコルーチンBを呼び出し そのBについても全て終えるか他コルーチン呼び出しで自動的にyield それぞれyieldして待機中の非同期コルーチンは呼び出し先が解決するとyield時点から再開 というスタックレス非同期コルーチンが何万も稼働しています
87 :デフォルトの名無しさん :2024/03/03(日) 13:45:11.38 ID:qMaLplcd.net 「コ」ルーチンなんだから上位という概念がそもそもない
88 :デフォルトの名無しさん :2024/03/03(日) 14:01:49.17 ID:IZl/xWS6.net >>87 スタックレス非同期コルーチンは上位ランタイムがCPUコアスレッド数をフルに使って幾万個の軽い非同期コルーチンをスケジューリングすることでCPU性能を使い尽くすことができる
89 :デフォルトの名無しさん :2024/03/03(日) 14:47:38.82 ID:qMaLplcd.net 的外れ
90 :デフォルトの名無しさん :2024/03/03(日) 14:53:35.32 ID:IZl/xWS6.net 的外れとは? 現在のネットインフラは>>88 の通り動いている現実を認めたくない?
91 :デフォルトの名無しさん :2024/03/03(日) 17:03:12.55 ID:Vr3nU8ht.net >>87 あなたの言うコルーチンの定義は?
92 :デフォルトの名無しさん :2024/03/05(火) 13:01:59.74 ID:hcPpBjWs.net スタックフルのコルーチンは、内部で呼び出したコルーチンがyieldしてるかどうか呼び出し元が知っていないといけない つまりカプセル化を破壊する
93 :デフォルトの名無しさん :2024/03/05(火) 14:58:20.75 ID:QuH/Int2.net 複数のスレッドが協調して何かするなんてのは、もはやプログラミング言語の領分じゃないんだよなぁ
94 :デフォルトの名無しさん :2024/03/05(火) 22:53:42.99 ID:1H/gFsr2.net Erlang OTPみたいなアプローチが良いと思う
95 :デフォルトの名無しさん :2024/03/08(金) 23:46:15.42 ID:7wk+t80g.net 重要なのはプロトコル 定められた型のメソッドを持つことが重要
96 :デフォルトの名無しさん :2024/03/11(月) 12:27:38.30 ID:HvPoaoEY.net 実装の継承は悪手
97 :デフォルトの名無しさん :2024/03/13(水) 21:40:17.77 ID:lx8X47UA.net 関数とオブジェクトなんて、要はどっちも値を入れたら何かを返す箱なのだから、内部構造を考えなければ共通化できるはず 配列も、機能だけ見れば、キーが数値なだけのただのオブジェクト リストやジェネレータなどをイテレートするのも、関数を引数なしで呼び出すようなもの
98 :デフォルトの名無しさん :2024/03/13(水) 23:38:55.71 ID:+S4Brq3S.net Pythonは__call__()メソッドを実装することでオブジェクトも関数のように振る舞える これは小規模なプログラムでは関数として実装しておき、大規模になったらシームレスに移行できるように
99 :デフォルトの名無しさん :2024/03/14(木) 18:36:51.37 ID:rbf7F1li.net Iterable⊂Callable⊃Associative だが、Iterableとしての呼び出し方とAssociativeとしての呼び出し方が異なる
100 :デフォルトの名無しさん :2024/03/14(木) 18:40:46.10 ID:rbf7F1li.net x = [1, 2, 3]をIterableとしてCallableだと思う場合、x() = next(x) AssociativeとしてCallableだと思う場合、x(n) = x[n] になる
101 :デフォルトの名無しさん :2024/03/14(木) 21:09:23.66 ID:xE50NtDY.net マシン語を知らないと妙なこだわりを言い出すからおもしろいよなw
102 :デフォルトの名無しさん :2024/03/14(木) 23:24:02.50 ID:yxqrN1vV.net 異なる層の話を出すのもどうかと思うがね
103 :デフォルトの名無しさん :2024/03/15(金) 08:26:32.50 ID:vBMVv6T3.net >>101 コンピュータサイエンスにおいて低レイヤーが上位という価値観があるのって、おそらく日本だけだと思うんだけど、どうしてこういう逆行現象が起こるんだろう?
104 :デフォルトの名無しさん :2024/03/15(金) 08:47:50.82 ID:0zJIPs3S.net 数学だと基礎論は馬鹿にされてるよね 通常の数学やるときに選択公理だとか不完全性定理だとかいってるのは素人だけ そりゃ形式的に「基礎論のほうが原理に近い」というだけならべつに数学を知らなくても分かるからね
105 :デフォルトの名無しさん :2024/03/15(金) 08:56:58.18 ID:wSiNosZi.net >>103 マシン語わかる でもコンピューターサイエンスの素養なし って感じのロートルが必至にマウントとろうとしてるだけだろな
106 :デフォルトの名無しさん :2024/03/15(金) 09:01:28.78 ID:0zJIPs3S.net ふつうは情報学科でも昔ならSICPでやったようなプログラミングの概念や機能に関する講義があるはずで、 ちゃんと勉強している人で実装と機能を分けて考えるということができない人はいないよ 日本で低レイヤーが偉そうな顔してるのは単に、大学等で体系的に学ばずに昔からパソコンに触ってただけのおじさんが言ってるだけ 日本ではプログラマが高等教育が必要な専門技術職として見なされておらず、パソコンしか取り柄のない社会不適合者を低賃金でこきつかう職種だと見なされていたのが原因
107 :デフォルトの名無しさん :2024/03/15(金) 09:10:45.64 ID:5Fl9dqIV.net >>104 物理学科の自称秀才がやたらと素粒子論専攻したがるのもこれだよなぁ…… 実際に勉強して内容に興味を持てたかどうかではなくて、ラベルで判断してる 20過ぎた大人が将来の進路に関わる選択でそういうことするのは、病的としか言いようがない
108 :デフォルトの名無しさん :2024/03/15(金) 12:38:47.19 ID:ohT/C40H.net >>99 ってことは>>31 の方法でも菱形継承問題ふつうに発生するやん Iterable < Callable __call__ = __next__ Associative < Callable __call__ = __getattr__ Array < Iterable, Associative __next__ = piyopiyo __getattr__ = hogehoge arr = Array(blahblahblah) arr() はどっちになるべき?
109 :デフォルトの名無しさん :2024/03/15(金) 14:11:38.88 ID:DTF+UIWv.net a) 実装継承 b) 多重継承 c) 多段階継承 ができる時点で菱形継承問題は避けられない Rubyは、クラスは(b)を、モジュールは(c)を諦めることでこの問題を回避している
110 :デフォルトの名無しさん :2024/03/15(金) 14:37:43.94 ID:zsH6n39D.net >>108 pythonはそうだよ
111 :デフォルトの名無しさん :2024/03/15(金) 16:37:06.04 ID:HEyD2qcN.net Rubyのrefinementsみたいに、このスコープでは型定義を変えるみたいなのがあればまだマシになんのかな たとえば、あるブロック内でだけ(Int, Int)をPoint2Dとみなすとか
112 :デフォルトの名無しさん :2024/03/15(金) 17:16:49.88 ID:Ratd2baE.net >>108 ・具象型とインタフェースを分ける 具象型→Int, Str, List<T>など インタフェース→Comparable, Iterableなど ・多重継承したインタフェースで仮想でないメソッドに名前の重複があったらエラーにする とするしかないのでは
113 :デフォルトの名無しさん :2024/03/15(金) 17:20:27.60 ID:Ratd2baE.net 型とインタフェースは分けないと、たとえば equal(x: Eq, y: Eq) -> bool とか無意味になる xとyには具体的な型が同じものが入ってほしいのに、Eqインタフェースを持っている型すべてを許容してしまう ほんとうにやりたいのは equal(x: T, y: T) -> bool where T: Eq ということ
114 :デフォルトの名無しさん :2024/03/15(金) 17:33:55.76 ID:UiCkupYq.net 型システムと並行性はむずかしいな でも、言語がプログラマに好まれるかどうかってそんなとこにないと思うよ 一時期、高機能なタスクランナーやら単体テストフレームワークやらがたくさん出てきたけど、結局はnpm-scriptsとか言語標準のunittestライブラリに収束した
115 :デフォルトの名無しさん :2024/03/15(金) 17:40:58.53 ID:UiCkupYq.net powershellはプログラミング言語としてはbashよりもはるかに高機能だが、誰もあれで書きたいとは思わない
116 :デフォルトの名無しさん :2024/03/15(金) 18:11:48.66 ID:RqiZ+uh9.net >>108 inheritanceを型に対して行うのが間違いな気がする Int x Intを、Point2DとRationalのサブクラスとしたとする Point2DとRationalの両方に + を実装(前者にはベクトルとして、後者には有理数として)したとして、 (1, 2) + (3, 4) はどうなるか? それは結局、今(1, 2), (3, 4)をどの型だとみなしているかによるのであって、型Int x Intの与り知らぬところだ
117 :デフォルトの名無しさん :2024/03/15(金) 18:25:53.61 ID:C8O2+ggF.net >>109 Mixinで多重継承問題回避できる理屈がよくわからんかったけど、今ようやっと理解できたわ
118 :デフォルトの名無しさん :2024/03/15(金) 18:31:58.44 ID:8+Y0uCh5.net >>113 結局Rustのトレイト境界が正解ということか
119 :デフォルトの名無しさん :2024/03/15(金) 19:43:07.16 ID:UT4yxmsS.net >>109 Javaの場合は、クラスは(b)を、インタフェースは(a)を諦めている
120 :デフォルトの名無しさん :2024/03/15(金) 21:35:17.15 ID:XzxHtHqU.net ダイヤモンド継承は、コンパイラが検出するのが正しい気がする 構文で回避するには、>>109 みたく機能を制限するしかない RustとHaskellはしてくれる
121 :デフォルトの名無しさん :2024/03/15(金) 21:53:50.95 ID:WMhMrtUW.net まあ、でもプログラミング言語が人気になるかどうかは >>114 の言うように、処理系の完璧さではなく、実用のニーズに耐え得ることなんだろうな
122 :デフォルトの名無しさん :2024/03/15(金) 22:34:31.87 ID:3o/QlmnN.net クロージャとオブジェクトが同等なのはわかったが、それよりもさらに強力な言語機能はないのか?
123 :デフォルトの名無しさん :2024/03/15(金) 23:30:35.88 ID:rbMui8Ez.net 継続 プログラマが直接触らないものとしては、ガベージコレクションとかレキシカルクロージャとかもそうかもね
124 :デフォルトの名無しさん :2024/03/16(土) 20:49:53.31 ID:TBzj9DHS.net Eiffel、Delphi、C#なんかは菱形継承してもそれぞれに別名を与えて捌ける機能を持ってるが オブジェクト指向自体が下火なのもあって、最近のやつはそこまで作り込まれてない感
125 :デフォルトの名無しさん :2024/03/16(土) 21:44:17.40 ID:gqfrZq4+.net 多重継承は名前がぶつかろうがそれ自体に問題は一切ない 抽象型から具象型への継承で実装継承でないならば区別できる限り問題は生じない 本質的な問題は実装継承であること 特にに具象型から具象型への継承が問題 これは多重かどうかに関係なく問題となる
126 :デフォルトの名無しさん :2024/03/16(土) 22:15:39.23 ID:ajdJdfuW.net そもそもOOPにおける継承ってのは、数学的に破綻してるんだよ >>116 で述べているとおり 型定義にサブタイピングの実装方法が入るわけがない
127 :デフォルトの名無しさん :2024/03/16(土) 22:18:29.76 ID:ajdJdfuW.net 自然数 is a モノイド と自然にみなす方法は2通りある その方法は自然数の定義とは関係ない
128 :デフォルトの名無しさん :2024/03/16(土) 22:28:23.06 ID:UqSB/mst.net クラスが欠陥品なのは同意だが クラスを捨てて型クラスを使えばよい
129 :デフォルトの名無しさん :2024/03/16(土) 22:30:52.34 ID:ajdJdfuW.net 行列式=1の2次正方行列が、 自分の型はM(2)なのか、GL(2)なのか、SL(2)なのかなんて知らんがな
130 :デフォルトの名無しさん :2024/03/16(土) 22:35:57.52 ID:TBzj9DHS.net >>127 の問題は型クラスやtraitでも発生するしなあ C++で没になったconcept_mapのようなものを複数切り替えられる機能が求められる
131 :デフォルトの名無しさん :2024/03/19(火) 03:55:14.66 ID:5WuD+qyw.net x: T where T: K where K: L where ... のように高階の型や fn(x: T) -> Type のようにパラメータに依存する型などがいくらでも作れたらどんなことができるのだろうか
132 :デフォルトの名無しさん :2024/03/19(火) 22:24:36.99 ID:TBPz9mXO.net >>131 具象型Tに対して抽象型Kの制約を課すところまでは普通として 抽象型Kに対して高階抽象型Lの制約を課すメリットが見つからなかった 代替の方法として高階とせずに 抽象型Kに対して同じレベルの抽象型Lの制約を常に課す宣言ができれば十分にみえる
133 :デフォルトの名無しさん :2024/03/20(水) 17:07:37.10 ID:yZelKyNv.net >>60 これを一般化したのが依存型やね これが扱えると数学の証明を型で書ける
134 :デフォルトの名無しさん :2024/04/06(土) 13:15:05.66 ID:IRVxBt67.net なんでもラムダ →なんでもオブジェクト →なんでも型
135 :デフォルトの名無しさん :2024/04/07(日) 01:46:27.75 ID:HXZiHVf3.net 全称量化子は関数で 存在量化子はパターンマッチングで 表せるので、一階述語論理もラムダ式で書けそう
136 :デフォルトの名無しさん :2024/04/07(日) 05:29:44.44 ID:FOjhJ4gr.net 書けるの意味がわからんな 書いてどうする? カリー・ハワード対応とかチューリング完全と関係ある話か?
137 :デフォルトの名無しさん :2024/04/07(日) 06:21:18.75 ID:ZeXbAXZK.net 存在命題とか背理法とか、仮定を仮引数にしたラムダ式を使えば、具体的に証明や値を構成しなくても所望のものを取ってこられるんだな
138 :デフォルトの名無しさん :2024/04/07(日) 07:33:56.22 ID:FOjhJ4gr.net それは理論的に不可能
139 :デフォルトの名無しさん :2024/04/07(日) 09:52:53.31 ID:/E0pQilp.net res = hoge()みたいにただ結果返すのと、 hoge((res) => doSomething())みたいに引数に結果入ってくるのと、何が違うんや
140 :デフォルトの名無しさん :2024/04/07(日) 10:45:31.26 ID:thR/d4RZ.net まず、hogeが同期的でない場合は res = hoge() とは書けない async/awaitのような機構が必要になる またたとえば、エラーの場合は処理を分岐させたいような場合、 hoge( (res) => doSomething(), (err) => handleError()) のように拡張できる ただし、内部でさらに同じようなことをやっているとコールバック地獄になる
141 :デフォルトの名無しさん :2024/04/07(日) 11:19:43.04 ID:0gYyGnNT.net >>134 依存型のあるHaskellやLean4はとくにオブジェクト指向という感じはしない
142 :デフォルトの名無しさん :2024/04/07(日) 13:08:22.06 ID:IfUr/96a.net >>141 純粋関数型で、(クラスベースの)オブジェクト指向をやる意味は皆無だからな
143 :デフォルトの名無しさん :2024/04/07(日) 19:27:40.79 ID:m+fa22Uj.net 純関数型言語でなくても モダンなプログラミング言語 Go、Rust、Zig、Nim、Julia、Elixirなどは クラスおよびその継承を言語仕様から排除しておりクラスは存在しない それら各々の言語は全く異なる方針を採っている言語だがクラス排除だけは全てが同じ方針である クラスとその継承は悪手であるとプログラミング言語界では結論が出ている
144 :デフォルトの名無しさん :2024/04/07(日) 20:16:17.84 ID:mSidkHeO.net だからってduck typingはクラスよりさらに悪いと思うんだ
145 :デフォルトの名無しさん :2024/04/07(日) 20:37:39.53 ID:rmfTjPEc.net >>144 だからクラスとダックタイピングを採用しない言語が増えているね
146 :デフォルトの名無しさん :2024/04/07(日) 21:18:11.49 ID:E193nq4c.net ポエムはよそでやれ
147 :デフォルトの名無しさん :2024/04/08(月) 14:29:36.32 ID:LhsijIe9.net P∧Qの導入則は、P -> Q -> P∧Q これは(P, Q)ならばP∧Qとも読めるし Pを仮定したとき、QならばP∧Qとも読める
148 :デフォルトの名無しさん :2024/04/09(火) 18:44:27.40 ID:kKsSVHOb.net 限定継続(reset/shift)の動きが意味わかんない resetで範囲絞ってる分call/ccより実用的には扱いやすいというのは分かるが、ローカルで見たらcall/ccのほうが直感的に思える
149 :デフォルトの名無しさん :2024/04/09(火) 23:18:54.13 ID:Aro4tJCD.net 実装の都合では? フル機能の継続はそこまでのスタック全部を生かし続けないといけないし分岐できないといけないしで スタック自体をOSの用意してくれるものとは別に自前で構成しないといけないしそうするとABIからなにからつらい
150 :デフォルトの名無しさん :2024/04/10(水) 01:07:10.63 ID:uPucvtCR.net 内部で継続渡しに変換してるなら、フルの継続のほうが実装しやすいと思うけど Schemeの場合、dynamic-windとかも実装しなきゃいかんからより複雑だろうけど
151 :デフォルトの名無しさん :2024/04/10(水) 03:30:06.71 ID:qIIVcoEj.net 継続渡しはスタック消費をクロージャで置き換えてるわけで、スタックを自前で用意してるのと同じようなもんでは
152 :デフォルトの名無しさん :2024/04/10(水) 04:04:38.17 ID:nlTt4/RM.net 会話が噛み合ってない
153 :デフォルトの名無しさん :2024/04/10(水) 09:16:26.52 ID:fNUeJXq8.net いや割と噛み合ってる
154 :デフォルトの名無しさん :2024/04/10(水) 11:55:16.47 ID:dXGnSmQj.net 処理が A → B → C → ... とあって A → ... → shift(fn k -> hoge) → B → ... → C → reset とすると k に処理 _ → B → ... → Cが束縛される hogeを実行するとresetまでジャンプする resetの値はhogeの値になる なので、たとえばバックトラックがしたいなら、 戻ってきたい場所にshiftを設置して、 fn () -> k next_valueをスタックに積んで、 クロージャの内部でk current_valueを実行すればいい
155 :デフォルトの名無しさん :2024/04/10(水) 11:57:37.66 ID:dXGnSmQj.net で、スタックから継続をpopして実行する関数を別に作って 選択肢がなくなったときはそれを呼べばいい
156 :デフォルトの名無しさん :2024/04/10(水) 16:24:50.24 ID:gx493tSk.net カプセル化を破壊するのが前提なので、ものすごく気を遣う
157 :デフォルトの名無しさん :2024/04/10(水) 17:47:04.13 ID:uxnW16Zl.net call/ccやreset/shiftの型はどうなるの?
43 KB
新着レスの表示
掲示板に戻る
全部
前100
次100
最新50
read.cgi ver 2014.07.20.01.SC 2014/07/20 D ★
本文 スレッドタイトル 投稿者