JavaScriptには関数の末尾再帰最適化がないので関数型には不向きだというデマ
冒頭報告
当方を不当に侮辱、誹謗中傷した人間から謝罪文が掲載されている。
nantonaku-shiawase.hatenablog.com
本論
関数型プログラミングが『銀の弾丸』であるという非常識な常識2022のなにがダメなのかわからない人が多いようなので、個人攻撃をまったくせずにダメ出しする。
また読まずにダメ出しするというアレが出てきた。
しかも、また致命的なレベルで勉強不足の分際で「しかし、簡単にダメ出しできる」とか宣っている。
こういう変な界隈の連中は非常に思い込みが激しいのと同時に、まともに勉強なんてしていないので、とんでもない勘違いワールド世界の住人である自覚もないのだが、ひとりで趣味でやっている分には「勝手にしろ」とか思うが、いざそれをこういう形で喧伝したり、ましてや他人様の著作にミソをつけるのが動機でやられたなら、こっちは黙っては居ない。
これまでこのパターンのアレな連中にさんざんこの類型の不条理なネガキャンをやられてきたし、問題は、それが間違ってると知ってる連中は黙ったままだし、問題のネガキャン当事者以下のアレはそろって、同意する、とかいって、あろうことかそのデマに「信憑性」のようなものを与えてしまう。非常に悪質である。
あの記事はめちゃくちゃ長いのに末尾再帰に触れていない
記事内を「末尾再帰」で検索してみよう。1か所もヒットしない。「末尾」でも1か所もヒットしない。そう、あの記事はめちゃくちゃ長いのに末尾再帰に触れていないのである。では「再帰」ならどうだろう。11か所ヒットした。しかし、具体的な再帰のコードはまったくない。長い記事内にあれだけ多数のコードを書いているにも関わらずである。
再帰については、まさに冒頭のreduceそのものに帰着して説明は完了しているので、特に必要ないかな、とは思ったが、ああやっぱ馬鹿な中級者はこういう角度から勘違いsて攻撃してくるんだ、とわかったので、このエントリ終わったら加筆に取り掛かってやろうとは思う。
末尾再帰の重要さ
「末尾再帰って何?」とか「再帰ってそんな重要なの?」と思う読者も多いだろうから、末尾再帰の重要さだけ説明しよう。
あの記事は、forやwhileを使わないプログラミング手法を前提に書かれている。記事内を「制御」とかで検索すればわかる。
末尾再帰はforやwhileの代わりになるもので、そういったプログラミング手法には欠かせない。forもwhileも末尾再帰も使わないとなると、ツリー探索などのアルゴリズムを書くことが困難になる。(こういったことが苦手な私に思いつく他の方法は、setIntervalを無理やりforループの代わりにするくらい)
「あの記事は 前提に書かれている。」
とか読んでもないやつがねー(苦笑
for や whileは、だから記事に書いているとおり、Fold/Reduceになるんだよ(ばーか)というか読めよ?
と言ったところだろうか。
というかなんでこいつら「読んでない」ということを誇らしげに言いながらこういうトンチンカンなこと言って生きてて平気なのだろうか?
異常者だと思う。
JavaScriptはたいてい末尾再帰をサポートしていない
そもそも、ほとんどのJavaScript実行環境は、末尾再帰をサポートしていない。つまり、JavaScriptはforやwhileを使わずに込み入ったプログラムをまともに書けるような言語ではない。あの記事に書いてあるようなことをする言語ではないのである。私は別にそれでもいいのでTypeScript使いまくってるけど。classとか好きだし。
あの記事がJavaScriptを使っている理由は、JavaScriptが人気だからだろうか?もしそうだとしてもダメである。あの記事は「JavaScriptは、ほどんどの実行環境が末尾再帰をサポートしていない、このプログラミング手法に適していない言語である」といったこと自体に触れていない。人気のある言語を使いたいなら、他の末尾再帰をサポートしている人気言語を使えばいい。
末尾再帰をサポートしていない、そのとおりだ。
そんなもんなくても関数型コード書けるし、末尾再帰が必要なコードっていうのは、理論的にはラムダ計算からの再帰ループを実装することでチューリング完全性を示したり、数学の漸化式の数列をそのまま実装することで理論的にはみるところはあるが、リアルなコードでそれをそのまま書くのは「アホ」だ。
これは、リアルなコードでYコンビネータをもってFold/Reduce式を書く人間がいないのと同じ意味で「アホ」なのだが、
Factorial(階乗計算)やらFibonacci数列のコードでは再帰の概念の説明に役立つのでサンプロコードとして示されていることは多くて、特にHaskellに足突っ込んでる系の中途半端に関数型プログラミングを知ってる「つもり」の中級者がひたすらそのアホなコードを再生産していて、以上の引用文のようなアホなデマを流していて甚だ有害である。
あの記事は「JavaScriptは、ほどんどの実行環境が末尾再帰をサポートしていない、このプログラミング手法に適していない言語である」といったこと自体に触れていない。
触れてないことなんて山ほどあるし、解説の流れの優先度ってもんがあるからね。
JSが末尾再帰をサポートしていない、なんてまったく重要な事柄ではないし、このプログラミング手法=関数型に適していない言語である、っていうのは、こういう勉強不足で知識不足の馬鹿連中の間違った知識にもとづくデマでしかない。
しかし、こういうデマを流して、しかも賛同する同類がいるとわかった以上、なるだけ早く触れざるをえないだろうということはわかった。非常に厄介である。
岡部さんの記事への反論。自分も筆者に同意する。>「関数型プログラミングが『銀の弾丸』」の記事がダメなところ https://t.co/T5ueeT18K4
— SEAHAL (@seahalcom) December 15, 2021
おまえがおかしいんだよ。
— ken (@ken80297274) December 19, 2021
おまえの勉強不足によって、僕の著作について不条理なデマ、誹謗中傷はじめたのはおまえだろーが。
こっちはそういうのでこれまで散々迷惑かってるので、今後は見逃さないし徹底的にやる。
おまえは侮辱されて当然の主張をしてるわけ。
かの有名な岡部さんとバトル中でござる。でも、拙者は末尾再帰は好きじゃないけど、無限ループを作り出す上で仕方なく使うという認識なのだが、岡部さんは末尾再帰はスタックオーバーフローとは無関係だという。
— SEAHAL (@seahalcom) December 18, 2021
だれも「末尾再帰はスタックオーバーフローとは無関係だ」なんて主張はしてないけどね。
— ken (@ken80297274) December 19, 2021
なんでおまえらはそうやって嘘の引用とかして平気ナノ?
Fold/Reduceの使い方を知らず、末尾先最適化が必要な再帰関数を書くしかないとおもってそういうコードを書くやつが低レベルでアホだと言ってるだけ。
以下、すでにこいつの返信は一部スパム認定されて見られないのでこちらの返信だけ引用する。しかしそれで十分である。
なんでろくに読みもしないで、駄目なところがどうとか、こんだけ上メセのわかった風の匿名連中ばっかで、同じ考えだとかいうアレが多いんだと思う?
— ken (@ken80297274) December 18, 2021
だいたいそういう言語のより好みをもって全否定してくるけど、factorialにしてもfibにしても末尾再帰のコード書くってこと?
— ken (@ken80297274) December 18, 2021
実際ディレクトリ構造とか知れた階層の再帰構造以外で再帰を活用しなければ解けない、JavaScriptじゃ無理な課題て何?
駄目な実例は出せる?
一応ブログや本で論評するから。
そもそも末尾再帰を使う必要もないのに、使うコードを書かなければいけないという思い込みがあるわけでしょ?
— ken (@ken80297274) December 18, 2021
あなたが考える末尾再帰を使わなければいけないような課題てなんですか?と質問している。
ディレクトリ構造など課題そのものが回帰は除く。それは階層は知れているので問題にならない。
思わないねメモリ消費が問題になるレベルの末尾再帰を使わないいけないような構造の課題って、たとえば何があるんですか?あなたは何に末尾再帰を利用してるんですか?と質問している。
— ken (@ken80297274) December 18, 2021
[1..] みたいなベクター値が解決できない
— ken (@ken80297274) December 18, 2021
言ってる意味がよくわからない。
もっと具体的にどうぞ。
だから、FactorialとかFibonacciとか漸化式の数列を求めるときに、再帰とかでやってるってこと?
— ken (@ken80297274) December 18, 2021
なんで?教科書にそういう例が乗ってたから?w
どんだけレベル低いんだよって話だよね笑
結局さ、末尾再帰ないと困るとかアホな主張してるやつらって、アホなコードしか書けないからなんじゃないの?ww
— ken (@ken80297274) December 18, 2021
いやまじで、自己参照の再帰のループでしかコード書けないとかおもってるんだ?
— ken (@ken80297274) December 18, 2021
どんだけレベルが低いんだよw
こちらの結論からいうと、
— ken (@ken80297274) December 18, 2021
javascript で関数型コードが書けない、とか断言してる連中はひとり残らず無知で、勉強不足だなとしか思っていないし、MLのやつにしても自身のその好きな言語でやれ、って言ってくるなんか自己中心的な連中ばっかで、いろいろ問題ありそうですね、なるほどなあ、て思う。
なんというか「混成だから」「堕落しちゃう」とか、属人的で、JavaScriptで関数型コードが書けない客観的な論証としては成立していない、というか、堕落した論調で、説得力ないな。
— ken (@ken80297274) December 18, 2021
大多数が否定なんて逃げないで自分の好みで否定してるてはっきり宣言すれば良いんじゃないかな。
別にletもconstも両方あるけどconstだけ使えばいいじゃない。
— ken (@ken80297274) December 18, 2021
純粋関数型言語じゃないと、関数型コードのメリットが享受できない、ってのは命題としたらFalseだと言ってる。
別にVSCodeという大規模開発の成果物でもなんでもTypeScriptで構築されてるけどね。
— ken (@ken80297274) December 18, 2021
君がHaskell好きとかマウンティングしてくるやつがML好きとか、結構どうでもいいけど、なんで他言語の関数型アプローチをそんなに否定したいの?
くだらない属人化までしてさ。そういう変な性格?
普通さ、大規模開発になると、コードスタイルも決まってて、あっちはOOPでこっちはFPなんてことは起こらない。
— ken (@ken80297274) December 18, 2021
ハイブリッドパラダイムではFPは無理なんて、狂った主張は、属人性によって正当化されるなんてことはないね。
別にJavaScriptに限らず、FPで階乗やらフィボナッチやら求めるのに、再帰ループ書かなければいけない、なんて縛りなんてないけどな。
— ken (@ken80297274) December 18, 2021
なんでそんなメモ化やら末尾再帰が必要になるようなパフォーマンスわるいコード書いて平気なの?
階乗の数列を求めるのに再帰なんて使ったら計算量が爆発して、メモ化でもしないとパフォーマンス落ちるでしょ。
— ken (@ken80297274) December 18, 2021
末尾再帰なんてもんは、そういうアホなコード書きたい人のために用意されてるんじゃないの?
別にやりたきゃやればいいけど、漸化式の数列求めるのに、再帰使わないとできないとか、低レベルの主張をしたのが君で、だから末尾再帰がない言語は、関数型アプローチで解決方法がないとかトンデモ主張してるのが君なんだけど。
— ken (@ken80297274) December 18, 2021
ツーか君、ループは全部再帰でやるのが関数型で純粋のHaskellはそれ一択とか思い込んでる?
— ken (@ken80297274) December 18, 2021
変な教科書読んで独学したの?
数学のFoldとかいう概念知らない?つかわない?
普通に考えても漸化式て、外側から多重に計算しながら、やる、みたいなもんは理論的には簡潔な定義ではあるが、計算するときは
— ken (@ken80297274) December 18, 2021
1x2=2
2x3=6
6x4=24
って手計算でもやるよねw
だれが再帰でメモ取りながら手計算するんだよ。
安い教科書の受け売りで自分で考えられないからそんなトンデモ君になるんだ。
計算機科学は数学がベースにあり、漸化式の数列を計算するのに末尾最適な再帰関数でしかできない、それ以外でどうやってやるのか?とか阿呆な質問してくるやつが、抜かすなよ。
— ken (@ken80297274) December 18, 2021
計算機学徒が聞いて呆れる。
ある学問がよりPrimitiveな学問がベースにある場合、たとえば関数型プログラミングはラムダ計算が基礎だが、そういうベース、基礎にある、ってことはそれぞれの存在の不要性を主張してるわけでもなんでもないよね。
— ken (@ken80297274) December 18, 2021
勉強不足の低レベルな自称計算機学徒が屁理屈ぶっこいて食い下がってネガキャンか?
僕が苛つくのは、曲がりなりにも「学徒」と自称するやつが、Foldで再帰構造をボトムアップに計算する、それは手計算でやるのと同じ方向の計算だし、もちろんメモ化も末尾最適化も両方必要ない。
— ken (@ken80297274) December 18, 2021
そんな基礎的なこともしらずに、末尾最適ないJSはFP無理だとかアホなデマ流して無自覚なこと。
ちょっとは恥じるってことはないわけ?
— ken (@ken80297274) December 18, 2021
学徒と自称するなら、
すみません、勉強不足でいいかげんなデマ流して他人様の著書に間違った誹謗中傷をしてしまいました。
勉強になりましたありがとうございます、すみません、じゃねーの?
-----
Haskell は勉強しても、飽和する感じがしないのよね。永遠に、理路整然としたわからないものが続くというか。
— SEAHAL (@seahalcom) December 17, 2021
まあたしかにそうだな。Haskell界隈の関数型の解説はかなり筋が悪い。永遠に、理路整然としてものが続くのは当たり前なんで、自分で理路整然としてものを書いたら、こういう馬鹿が読みもしないでネガキャンの挑戦してくるわけだ。
はなし聞いてた?
— ken (@ken80297274) December 18, 2021
Fold/Reduceつかえば、recursiveな計算できるって、Wikipediaのタイトル記事見せたのに?
大丈夫?
質問するのは自由だけど、与えた情報咀嚼できない人?
ちなみに見せた記事はこれ
https://t.co/o7HJmaMU0S
— ken (@ken80297274) December 18, 2021
In FP, fold (..) refers to a family of higher-order functions that analyze a recursive data structure and through use of a given combining operation, recombine the results of recursively processing its constituent parts, building up a return value.
In functional programming, fold (also termed reduce, accumulate, aggregate, compress, or inject) refers to a family of higher-order functions that analyze a recursive data structure and through use of a given combining operation, recombine the results of recursively processing its constituent parts, building up a return value. Typically, a fold is presented with a combining function, a top node of a data structure, and possibly some default values to be used under certain conditions. The fold then proceeds to combine elements of the data structure's hierarchy, using the function in a systematic way.
関数型プログラミングでは、fold(reduce, accumulate, aggregate, compress, injectとも呼ばれる)とは、再帰的データ構造を解析し、所定の結合操作によって、その構成要素を再帰的に処理した結果を再結合して戻り値を構築する高階関数群のことである。
通常、foldには、結合関数、データ構造のトップノード、および場合によっては特定の条件下で使用されるいくつかのデフォルト値が提示される。そして、foldは、その関数を体系的に用いて、データ構造の階層の要素を結合していく。
Fold/reduce「再帰的データ構造」を結合操作する高階関数、これは本書でもじっくりと書いたこと。
On lists[edit]
The folding of the list [1,2,3,4,5]
with the addition operator would result in 15, the sum of the elements of the list [1,2,3,4,5]
. To a rough approximation, one can think of this fold as replacing the commas in the list with the + operation, giving 1 + 2 + 3 + 4 + 5
.
In the example above, + is an associative operation, so the final result will be the same regardless of parenthesization, although the specific way in which it is calculated will be different. In the general case of non-associative binary functions, the order in which the elements are combined may influence the final result's value. On lists, there are two obvious ways to carry this out: either by combining the first element with the result of recursively combining the rest (called a right fold), or by combining the result of recursively combining all elements but the last one, with the last element (called a left fold). This corresponds to a binary operator being either right-associative or left-associative, in Haskell's or Prolog's terminology. With a right fold, the sum would be parenthesized as 1 + (2 + (3 + (4 + 5)))
, whereas with a left fold it would be parenthesized as (((1 + 2) + 3) + 4) + 5
.
In practice, it is convenient and natural to have an initial value which in the case of a right fold is used when one reaches the end of the list, and in the case of a left fold is what is initially combined with the first element of the list. In the example above, the value 0 (the additive identity) would be chosen as an initial value, giving 1 + (2 + (3 + (4 + (5 + 0))))
for the right fold, and ((((0 + 1) + 2) + 3) + 4) + 5
for the left fold. For multiplication, an initial choice of 0 wouldn't work: 0 * 1 * 2 * 3 * 4 * 5 = 0
. The identity element for multiplication is 1. This would give us the outcome 1 * 1 * 2 * 3 * 4 * 5 = 120 = 5!
.
まあこの辺のことは、Wikipediaのタイトル記事(別に数学的にWikipediaがおすすめ記事と言ってるわけではない、Wikipedia記事であってさえも、というニュアンスがある)でも、普通に記載されている。
には、
再帰呼出しの例[編集]
ここでは、階乗計算を再帰呼び出しにより実装する例を紹介する(自身の再帰呼び出しが、その計算における最後のステップになっているような末尾再帰は再帰呼び出しの特別なケースであることに注意)。
C言語での例:
という、コードが掲載されている。
実はこれは無名関数=ラムダ式であってもこのような再帰関数を定義することはできて、それがラムダ計算だけでも「繰り返し処理」ができるという重要な証明となっていて、チューリング完全だ、とか理論上は重要だ。
それは大変結構なことなのだが、理論上の重要性だけにとどめおかないで、これが階乗計算の関数型スタイルの作法だ、みたいに伝言ゲームをやらかしているアホな教科書や教師、そしてそれを鵜呑みにして疑わない、この記事で紹介した中級者みたいな2人あるいは3人のような人間がいる。
リアルのコードで階乗計算にトップダウン方向の再帰関数を書くようなプログラマーは「アホ」である。
それこそ末尾再帰のメモリ効率化の問題やら、なんども非効率に同じ計算が指数関数的増加するのでメモ化が必要になって、デメリットしかない。
課題そのものがディレクトリ構造のように再帰構造ならばまだ許容できるが、
本書で掲載したコードを引用すると、
と、普段普通に階乗の手計算でやるのと同じボトムアップでFoldするreduceを使えば良いことだ。そしてこれは本書の冒頭からずっと継承して説明しつづけているコードでもある。
本人の該当ツイートが消えているのが残念だが、それが上記のツイートであり、特に彼は「他にどういうやり方があるんですか?」みたいに聞いてきたので、その返答が、これである。
ツーか君、ループは全部再帰でやるのが関数型で純粋のHaskellはそれ一択とか思い込んでる?
— ken (@ken80297274) December 18, 2021
変な教科書読んで独学したの?
数学のFoldとかいう概念知らない?つかわない?
続きをかくと、
最初の最初に、どういう事例があるんだ?って質問したのが僕で、その事例として漸化式の数列計算を出してきたのが君で、それは末尾最適化のある再帰関数でしかメモリの問題は解決できないとか、トンデモ主張をして、僕が鼻で笑った、っていうのがこれまでの流れ。
— ken (@ken80297274) December 18, 2021
いいよ?最初に戻りたいのなら。やれ
「チューリング完全ならば、同じことができる」そのとおりだけど、聞いてて思ったけどFoldのrecursiveな用法の知識すらないやつが、大風呂敷広げてもなあ、と鼻で笑い続けてるよ。
— ken (@ken80297274) December 18, 2021
御託はいいから、さっさとやってみたら?
現状君ひとりが右往左往して恥さらしてるだけなんだから。
オーケーじゃないよ。
— ken (@ken80297274) December 18, 2021
最初の最初から末尾最適がないことによって解決不能な課題ってなんだ?と質問しているの。
で、一発目に出してきたのがRecursiveな数列で、Recursiveな数列はRecursiveなFoldで計算できる、と君が恥をかいた。だから頑張れよ?
パッとツイートも出来ないのか。
— ken (@ken80297274) December 18, 2021
えーっとどれくらい?
時間もかかるんなら、あらかじめそう言っといて。
漸化式の数式の計算は末尾最適化されてないと問題があるみたいなトンデモ主張はさっとだせるんだから、同じレベルで別のなんか勉強不足のまま勘違いして覚えてデマながして学徒として恥も知らないみたいな事例がすぐデてくるんじゃないのか。
— ken (@ken80297274) December 18, 2021
今から考えるってこと?
なんでPCが必要なの?
問題はコラッツ予想の再帰を Functional Programming in JavaScriptで書いた場合と、命令形言語よろしく while で書いた場合の比較ですよ。末尾再帰を使わないと、前者は後者よりも求められる数が制限されるはずです。
— SEAHAL (@seahalcom) December 18, 2021
だから繰り返しになるけど、命令型のループは、関数型のfold/reduceに置換されるのだし、ImmutableJSというライブラリ使ったら無限数列の遅延操作もできるので、そんな命令型で出来て関数型で出来ない問題など発生しない。
— ken (@ken80297274) December 18, 2021
まあチャレンジしてみたら?
FPinJSの堅牢さの証明になるメリットはあるから。
で、これどうなってるの?
— ken (@ken80297274) December 18, 2021
負けを認めろみたいに言ってきたわけだし、学徒自称するなら、すみません間違いでしたデマ流してしまいました。勉強になりました、ありがとうございます、すみません、じゃないの?
人の著作誹謗中傷してほったらかし?
諦めたんじゃなかったのか。1週間位?
— ken (@ken80297274) December 19, 2021
年内にカタはつけてね。
おい、岡部健に粘着されているんだが、どうしてくれるのだ!ただでさえ、関数型言語の人には変な連中が多いのに、どうして彼はこんなにまでキチガイじみた攻撃性を持って侮辱してくるのだろうか?それとも、俺がおかしいのか?気が狂ってしまいそうだ!
— SEAHAL (@seahalcom) December 18, 2021
だから、パソコンが手元にないから時間がかかるっていうだけだろ。そうカッカするなよ。
— SEAHAL (@seahalcom) December 18, 2021
とりあえず一旦UPする。
どうせ本書に追記しようとはおもうので、
ImuutableJSを使ったコードはどっかに残ってるはずだし、コピペできるかもしれない。