It is well-known that closures and objects are equivalent. So are actors. Which one is the most fundamental?
よく知られたように、クロージャとオブジェクトは等価です。アクターも同様です。どの概念が最も基本的なのでしょうか?
探検
Closures vs Objects
2024/02/19(月) 20:39:15.91ID:VT95BnI9
2024/02/19(月) 20:49:06.93ID:VT95BnI9
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.
クロージャはローカル・スコープに状態をもつ関数です。
オブジェクトもまたそのインスタンスに属するメソッドと状態を持ちます。
これらは互いに互換性があります。
An object also has methods and state which belong to its instance.
These are compatible with each other.
クロージャはローカル・スコープに状態をもつ関数です。
オブジェクトもまたそのインスタンスに属するメソッドと状態を持ちます。
これらは互いに互換性があります。
2024/02/20(火) 09:36:12.34ID:fQy0X2aE
オブジェクトは複数のメソッドを持てる
クロージャは関数をひとつしか返せない
オブジェクトの勝ち
クロージャは関数をひとつしか返せない
オブジェクトの勝ち
2024/02/20(火) 11:01:18.21ID:BFMbzAij
>>3
ハッシュテーブルに関数を追加して返すことで、複数のメソッドを返すことが可能
より柔軟に、オブジェクトとそれを操作する関数の組を返すこともできるし、無限個のメソッドをもったオブジェクトだって作れる
これらは書く対象によって使い分ければよいのであって、オブジェクトにメソッドが属しているのが常に最適とは限らない
たとえばイベントハンドラにコールバック関数として渡すような場合は、ひとつの関数として取得できたほうが便利
ハッシュテーブルに関数を追加して返すことで、複数のメソッドを返すことが可能
より柔軟に、オブジェクトとそれを操作する関数の組を返すこともできるし、無限個のメソッドをもったオブジェクトだって作れる
これらは書く対象によって使い分ければよいのであって、オブジェクトにメソッドが属しているのが常に最適とは限らない
たとえばイベントハンドラにコールバック関数として渡すような場合は、ひとつの関数として取得できたほうが便利
2024/02/20(火) 23:29:16.13ID:0cXn/136
クロージャのほうがprimitive
今時、高階関数やイテレータを書くのにわざわざデザインパターンとか使いたいかって話
クラスは、レキシカル環境つきオブジェクトだけが欲しくても必ず新しい名前空間が作られるし、単品で購入できない福袋みたいなもん
今時、高階関数やイテレータを書くのにわざわざデザインパターンとか使いたいかって話
クラスは、レキシカル環境つきオブジェクトだけが欲しくても必ず新しい名前空間が作られるし、単品で購入できない福袋みたいなもん
2024/02/21(水) 05:55:14.17ID:j79lZ4PO
二変数以上の関数のほとんどは、どの引数のオブジェクトに属しているともアプリオリには言えないし、オブジェクト指向は不自然
オブジェクトと引数は別物だし、もし関数を特定のオブジェクトに属するようにしたいなら、クロージャでラップすれば簡単にできる
オブジェクトと引数は別物だし、もし関数を特定のオブジェクトに属するようにしたいなら、クロージャでラップすれば簡単にできる
2024/02/21(水) 05:56:37.93ID:j79lZ4PO
二変数以上の関数のほとんどは、どの引数のオブジェクトに属しているともアプリオリには言えないし、オブジェクト指向は不自然
オブジェクトと関数は別物だし、もし関数を特定のオブジェクトに属するようにしたいなら、クロージャでラップすれば簡単にできる
オブジェクトと関数は別物だし、もし関数を特定のオブジェクトに属するようにしたいなら、クロージャでラップすれば簡単にできる
2024/02/21(水) 10:09:33.92ID:HBySQ9lj
2変数関数をクロージャにラップして1変数関数にしても、メソッドチェーンはできないよね
2024/02/21(水) 10:29:18.43ID:Cg6r5DK9
[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))
[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))
2024/02/21(水) 12:32:59.23ID:pWazjr0Z
>>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
これでどうよ
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
2024/02/21(水) 15:43:45.17ID:im/wtuEl
一時期メタプログラミングとかいって、動的にクラスやメソッドを作れるのがすごいって言われてたけど、クロージャをデータ構造として使うならそれは自明にできることでは
2024/02/21(水) 16:28:56.20ID:4iB0jwLo
やり方が複数ある場合は、一番綺麗に書けるコードを採用するのが基本
2024/02/21(水) 22:54:29.50ID:DX/jvS2m
入れ子にしなくても関数合成でいいのでは?
メソッドチェーンっぽく書くには
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
メソッドチェーンっぽく書くには
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.21ID:seCGgTJE Forth で書いてよ
2024/02/22(木) 08:01:15.94ID:5dknMZXg
>>13
カリー化のなせる技やね
カリー化のなせる技やね
2024/02/22(木) 08:40:34.15ID:GWomKHpC
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
いろいろ書けるんだな
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
いろいろ書けるんだな
2024/02/22(木) 10:55:42.37ID:2PGen9gt
カリー化もクロージャと可変長引数があればできるね
currying = (f, ...args) => (...rest) => f(...args, ...rest)
currying = (f, ...args) => (...rest) => f(...args, ...rest)
2024/02/22(木) 11:04:45.60ID:2PGen9gt
これで、mapなどが
map(f, col)
と、関数を先にとる形でも、>>16のchainlを用いて
chainl([1, 2, 3, 4, 5])(
currying(filter, (x) => x % 2))(
currying(map, (x) => x * x))(
sum)()
と書けるわけか
map(f, col)
と、関数を先にとる形でも、>>16のchainlを用いて
chainl([1, 2, 3, 4, 5])(
currying(filter, (x) => x % 2))(
currying(map, (x) => x * x))(
sum)()
と書けるわけか
2024/02/22(木) 11:11:29.91ID:2PGen9gt
いや、単にchainlを
chainl = (obj) => {
return (f, ...arg) => {
if (!f) return obj
return chainl(f(...arg, obj))
}
}
とすればいいのか
chainl = (obj) => {
return (f, ...arg) => {
if (!f) return obj
return chainl(f(...arg, obj))
}
}
とすればいいのか
2024/02/22(木) 12:04:47.68ID:0KLGqPZR
クロージャの真価はコンテキストを導入できること
(let ((a 1) (b 2)) (+ a b))
((lambda (a b) (+ a b)) 1 2)
は等価
なので、上のa + bの変わりに関数を返却すれば独立したスコープの状態をもつ何かを作れる
クラスベースのオブジェクト指向でも同様だが、そのようなコンテキストのテンプレート(= class)は静的にしか作れない
(let ((a 1) (b 2)) (+ a b))
((lambda (a b) (+ a b)) 1 2)
は等価
なので、上のa + bの変わりに関数を返却すれば独立したスコープの状態をもつ何かを作れる
クラスベースのオブジェクト指向でも同様だが、そのようなコンテキストのテンプレート(= class)は静的にしか作れない
21デフォルトの名無しさん
2024/02/22(木) 12:15:29.98ID:Q3haYGVO 静的なほうがバグの発見は早そう
2024/02/22(木) 13:03:51.54ID:BdBf3uc1
なんでも自由を許すと無秩序になる
>>10だって、シグネチャを無視することが前提の設計になってる
(最後に結果を呼び出す時は第一引数のfを省略する)
TypeScriptが使われるのもそれが理由
今やPythonやRubyにも型がある
>>10だって、シグネチャを無視することが前提の設計になってる
(最後に結果を呼び出す時は第一引数のfを省略する)
TypeScriptが使われるのもそれが理由
今やPythonやRubyにも型がある
23デフォルトの名無しさん
2024/02/22(木) 13:23:57.40ID:v9QmWW2c2024/02/22(木) 13:24:08.05ID:WrtsfcL/
型なんて、プログラマがちまちま書かずとも、コンパイラや型チェッカが実行前に検査してくれればいいと思う
2024/02/22(木) 15:02:53.21ID:bbu+R1aP
currying = (f) => {
args = []
apply = (a) => {
if (a === undefined) return f(...args)
args.push(a)
return apply
}
return apply
}
args = []
apply = (a) => {
if (a === undefined) return f(...args)
args.push(a)
return apply
}
return apply
}
2024/02/22(木) 18:39:34.82ID:SfbO+Gz+
クロージャはポリモーフィズムができない
オブジェクト指向言語ではたとえば比較演算子の動作を型によって変えられる
a.compare(b)とソースコードのどこに書いても、aの型に応じて適切にcompareの動きが選択される
変数の型によって実行する関数を変えるにはどうする?
まさか関数定義に型による条件分岐をいちいち書くわけじゃあるまいし
オブジェクト指向言語ではたとえば比較演算子の動作を型によって変えられる
a.compare(b)とソースコードのどこに書いても、aの型に応じて適切にcompareの動きが選択される
変数の型によって実行する関数を変えるにはどうする?
まさか関数定義に型による条件分岐をいちいち書くわけじゃあるまいし
27デフォルトの名無しさん
2024/02/22(木) 19:07:57.79ID:v9QmWW2c class構文が入るまでどうやってJSでクラスを定義してたのか調べれば分かるよ
2024/02/22(木) 19:20:20.77ID:bu5i+qrs
>>27
a.hoge()のようにしてダックタイピングするなら当然できる
hogeがa(or a.prototype)に属さないただの関数の場合でも
hogeの中で型による条件分岐を書く以外の方法でできるか
a.hoge()のようにしてダックタイピングするなら当然できる
hogeがa(or a.prototype)に属さないただの関数の場合でも
hogeの中で型による条件分岐を書く以外の方法でできるか
2024/02/22(木) 19:30:27.02ID:coyprm7i
30デフォルトの名無しさん
2024/02/22(木) 19:41:55.54ID:v9QmWW2c >>28
なんて?
なんて?
2024/02/22(木) 20:46:51.05ID:5LGf2QVl
>>26,28
a, bではなくcompareのほうを、状態をもった関数オブジェクトとして生成する
一例として、
compareのクロージャには、「型名=>その型の引数を受け取った時に呼び出す関数」のテーブルを保持する
そのテーブルに関数を動的に登録するための関数を作成する
例:
IComparable = () => {
dispatch = {}
compare = (self, other) => { /*dispatchテーブルから関数を検索して実行*/ }
implement = (type, fn) => { /* dispatchテーブルにfnを登録 */ }
return [compare, implement]
}
もちろんdispatchの条件は型名じゃなくても、別の判定用関数を用意してもいい
a, bではなくcompareのほうを、状態をもった関数オブジェクトとして生成する
一例として、
compareのクロージャには、「型名=>その型の引数を受け取った時に呼び出す関数」のテーブルを保持する
そのテーブルに関数を動的に登録するための関数を作成する
例:
IComparable = () => {
dispatch = {}
compare = (self, other) => { /*dispatchテーブルから関数を検索して実行*/ }
implement = (type, fn) => { /* dispatchテーブルにfnを登録 */ }
return [compare, implement]
}
もちろんdispatchの条件は型名じゃなくても、別の判定用関数を用意してもいい
2024/02/22(木) 20:49:14.36ID:5LGf2QVl
インタフェースの実装は、型定義に書くよりも使用時に動的にできた方が自然だ
たとえば木構造にイテレータを実装するとして、深さ優先探索にするのか幅優先探索にするのかは、その時々によって変わる。木構造の定義だけからは決まらない
たとえば木構造にイテレータを実装するとして、深さ優先探索にするのか幅優先探索にするのかは、その時々によって変わる。木構造の定義だけからは決まらない
2024/02/22(木) 22:14:42.54ID:BzyFji/L
最近のフレームワークはどんどん脱オブジェクト指向・関数型化してないか?
Pythonのフレームワークは機能のMixinは継承ではなくデコレータ(ようはクロージャ)によるものが多い
Reactなんか思想は元々関数型的だったけど、ついにクラスコンポーネントやめちゃった
Pythonのフレームワークは機能のMixinは継承ではなくデコレータ(ようはクロージャ)によるものが多い
Reactなんか思想は元々関数型的だったけど、ついにクラスコンポーネントやめちゃった
2024/02/23(金) 00:14:17.41ID:vwgp24xa
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)
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)
2024/02/23(金) 13:52:03.15ID:P8ZW/vvm
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.
Objects are structures with contexts.
A structure can have functions as its member, and vice versa.
So both are equal.
2024/02/23(金) 19:20:29.47ID:mZKLNS+O
オブジェクトよりも、クロージャよりも、第一級継続のほうが強い
37デフォルトの名無しさん
2024/02/24(土) 00:22:58.31ID:PF21lJOY >>36
パフォーマンスは?
パフォーマンスは?
2024/02/24(土) 06:28:09.62ID:dndVDS6q
39デフォルトの名無しさん
2024/02/24(土) 11:30:44.99ID:PF21lJOY >>38
その回答、第一級継続関係無いやん。
その回答、第一級継続関係無いやん。
2024/02/24(土) 21:14:52.39ID:4BGFiybC
オブジェクトとクロージャが使えることは、コードブロック(+ ローカル変数)を第一級オブジェクトに持っているのだと思える
継続が第一級オブジェクトなのは、プログラム実行時の任意の状態が第一級オブジェクトということ
継続が第一級オブジェクトなのは、プログラム実行時の任意の状態が第一級オブジェクトということ
41デフォルトの名無しさん
2024/02/24(土) 21:42:30.15ID:fbMBQzh4 >>40
継続使う場合のパフォーマンスは?
継続使う場合のパフォーマンスは?
2024/02/25(日) 00:23:07.77ID:bNSutqDu
2024/02/25(日) 09:10:18.70ID:zl+9FS4I
継続はクロージャで表現されるけど、オブジェクトとクロージャが同値なら、オブジェクトによる表現もできるの?
44デフォルトの名無しさん
2024/02/25(日) 10:52:31.35ID:5QMstwYR : fact dup
1 = if
drop
else
dup 1- rot rot *
swap recurse
then
;
1 5 fact cr .
1 = if
drop
else
dup 1- rot rot *
swap recurse
then
;
1 5 fact cr .
2024/02/25(日) 11:03:04.14ID:O8qt4uJC
インタフェースが静的に決まれば保守性はおちないと思う
46デフォルトの名無しさん
2024/02/25(日) 11:17:17.62ID:azDE+Je3 近年まれにみる良スレ
2024/02/25(日) 11:28:28.39ID:PRxvM5Hr
>>31
これ継承の問題点を全部解消してる上に継承よりも強力だな
これ継承の問題点を全部解消してる上に継承よりも強力だな
48デフォルトの名無しさん
2024/02/25(日) 16:27:53.33ID:4fScDWQR ハァ?
2024/02/25(日) 16:36:21.69ID:wVCQJTWx
2024/02/25(日) 17:38:12.08ID:EvE7ghxV
2024/02/25(日) 17:55:13.30ID:BE8i65fG
>>43
できるだろうけど、継続自体が手続き/関数的な概念なので、オブジェクトによる表現をするメリットは少ないと思う
できるだろうけど、継続自体が手続き/関数的な概念なので、オブジェクトによる表現をするメリットは少ないと思う
2024/02/25(日) 19:51:34.20ID:O9l+7I5E
継続とコルーチンなら、継続のほうがprimitiveなの?
2024/02/25(日) 20:50:19.80ID:ZY2lsWgE
>>52
継続が第一級なら保存した継続を何度も呼び出せるが、コルーチンはyieldしたらもうその時点には戻れないので、継続のほうが根本的
継続が第一級なら保存した継続を何度も呼び出せるが、コルーチンはyieldしたらもうその時点には戻れないので、継続のほうが根本的
2024/02/26(月) 12:34:53.26ID:9feDQZG5
なんでもクロージャだと戻り値全部関数だけど、型情報を持つことはできる?
ディスパッチするのに必要だよね?
ディスパッチするのに必要だよね?
2024/02/26(月) 15:19:09.55ID:L4EYeETV
>>54
type属性をもったオブジェクトとして返却すればいいんじゃないんですかね
それを毎回書きたくないなら、生成用関数を受け取って新しい生成用関数を返す関数
makeConstructor = (f) => (type, ...args) => {type: type, value: f(...args)}
みたいなのを使ってみてはどうでしょうか
type属性をもったオブジェクトとして返却すればいいんじゃないんですかね
それを毎回書きたくないなら、生成用関数を受け取って新しい生成用関数を返す関数
makeConstructor = (f) => (type, ...args) => {type: type, value: f(...args)}
みたいなのを使ってみてはどうでしょうか
2024/02/26(月) 18:51:01.76ID:k7j7cG9/
>>55
これ、オブジェクトからtype属性つきオブジェクトへの関数、を持ち上げる関数書けばモナドになるから自動的に変換できるんだな
これ、オブジェクトからtype属性つきオブジェクトへの関数、を持ち上げる関数書けばモナドになるから自動的に変換できるんだな
レスを投稿する
ニュース
- 【サッカー】U-17日本代表、激闘PK戦制す 北朝鮮撃破で6大会ぶり8強入り U17W杯 [久太郎★]
- 「国民の憤りを引き起こした」中国側“高市首相発言の撤回改めて要求” [どどん★]
- 【インバウンド】中国からの“渡航自粛”…ツアー1000人分の直前キャンセル「キャンセル料は免除してくれ」 ことしいっぱいキャンセルに [1ゲットロボ★]
- 【芸能】日中関係悪化でエンタメ業界に大ダメージ… JO1の中国でのイベント中止、邦画は公開延期、STARTOアイドルへの影響も [冬月記者★]
- XやChatGPTで広範囲の通信障害 投稿や閲覧できず [蚤の市★]
- 【サッカー】日本代表、ボリビアに3発快勝 森保監督通算100試合目を飾る…鎌田、町野、中村がゴール [久太郎★]
- Perfume・あ~ちゃんの結婚相手の一般男性、吉田カバンの社長と判明 [977261419]
- 急に真冬かよ!!!⛄❄✨
- 地球から無限km先の場所ってどうなっているの?
- 自民党議員「高市は先人が築き上げた日中関係を壊した。外務省が謝罪に言ってるが自分で責任を取れ」 [834922174]
- 🖐( -᷄ὢ)俺に挑むのはやめておけ……実力差がありすぎる
- 日本、高市のお陰で破滅に近づくwwwwwwww
