質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

1348閲覧

javaScript コード、解説をお願いします

Behemoth

総合スコア29

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

1グッド

1クリップ

投稿2018/04/26 07:57

編集2018/04/26 08:30

前提・実現したいこと

「codewars]というサイトを使ってjavaScriptを学習しているものです。
コードを解説していただきたく存じます。

add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10
add(1)(2)(3)(4)(5); // 15
このように、カッコでくくった数字をチェーンのようにつなげて、その合計を出力する関数addを作成せよ、という課題がありました。
https://www.codewars.com/kata/a-chain-adding-function/javascript

模範解答は

function add(n){ var fn = function(x) { return add(n + x); }; fn.valueOf = function() { return n; }; return fn; }

とのことでした。
ここで、valueOf をオブジェクトのプロパティとすると、基本型の値と結合する際に、valueOfの中身が返される、と習い、そこがポイントかと思うのですが、理解できませんでした。

どなたか解説をよろしくお願いいたします。

miyabi-sun👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2018/04/26 08:10

質問テンプレート部分の文言が多く残っています。質問内容や意図を読み取る上ではノイズにしかなりませんので、自身の質問に関係のある文章のみ残してください。また、プログラムコード(およびエラーメッセージ)は```で囲ってください。(わからなければ質問編集画面でコード部分を選択し<code>ボタンを押してください)正しく反映されているかどうかは質問編集画面のプレビューを見ながら編集していってください。
Behemoth

2018/04/26 08:28

承知しました! ご説明いただきありがとうございます。
Lhankor_Mhy

2018/04/26 08:52

この模範解答って、みなさん動作してます?
namimon

2018/04/26 09:00

関数が戻ってきます
Lhankor_Mhy

2018/04/26 09:04

ですよね。数値で受け取るには評価しなきゃいけないから、true || add(1) みたいなおまじないを書かないと……
guest

回答1

0

ベストアンサー

おー、この回答はちょっと思いつきませんでした。
素敵ですね…覚えておいて今度どっかで使おう。

JavaScript

1function add(n){ 2 var fn = function(x) { 3 return add(n + x); 4 }; 5 6 fn.valueOf = function() { 7 return n; 8 }; 9 10 return fn; 11} 12 13console.log( 14 Array(100000) 15 .fill(0) 16 .map((_, i) => i + 1) 17 .reduce((sum, it) => sum(it), add) 18 .valueOf() 19) 20// 5000050000

そして使い方はこうですか。
はい、大体理解したので早速解説に移ります。


addは関数です。
中身でfnという関数を宣言して、fn.valueOfというプロパティをくっつけてすぐに返します。

2回目以降が素晴らしくて、
2回はfnに代入された引数がxの無名関数が実行されます。
その結果はreturn add(n + x)

足し算した結果が再帰関数のようにaddに転がり込み、
fnを作成、valueOfプロパティをくっつけて帰ってきます。

3回目以降は全て2回目と同様です。
何回呼び出しても今作ったfnが帰ってきます。

でもこいつ関数じゃん、合計結果の数値が欲しいんだけど?
最終的に値を取り出す時の事を想定してvalueOfが用意されているようです。
値を取り出す時はプロパティのvalueOfを使って取り出しましょう。

JavaScript

1console.log(add(1)(2)(5)); 2// function 3console.log(add(1)(2)(5).valueOf()); 4// 8

おまけ: valueOfプロパティの効果

命題がadd(1)(2)(3)…と無限にチェーン出来るという事は、関数を返し続ける関数である必要があります。
じゃあ値を取り出したくなってもよくわかんないよね…
今回の模範解答はたまたまvalueOfが宣言されていたのだけど……

JavaScriptにはインスタンス化されたオブジェクト等、
Stringとして表せられないものには大抵valueOfというメソッドが用意されており
何時でも値に変換出来るようにするというルールがあります。

JavaScript

1function add(n){ 2 var fn = function(x) { 3 return add(n + x); 4 }; 5 6 fn.valueOf = function() { 7 return n; 8 }; 9 10 return fn; 11} 12 13// console.logしたらf 6が表示されるけどなんだ? 14console.log(add(1)(2)(3)) // f 6 15 16// 足し算すると普通にNumber型っぽく計算に使える!? 17console.log(10 + add(1)(2)(3)) // 16 18 19// valueOfがあればなんでもいけんの?じゃあこれは? 20console.log(10 + { 21 name: 'taro', 22 age: 17, 23 valueOf: function(){ return this.age } 24}) 25// 27 26 27// 配列とかどうすんだ? 28var arr = [1, 2, 3]; 29arr.valueOf = function(){ 30 var sum = 0 31 for (var i = 0; i < this.length; i++) { 32 sum += this[i] 33 } 34 return sum 35} 36console.log(10 + arr) // 16

こういう効果があります。
この辺も含めると、質問文のコードは模範解答となるのは間違いないでしょう。

ただ、add(1)(2)の結果が3という元ネタサイトの記述は違和感がありますけどね。
まぁconsole.logを使って「f 3」と表示されるのはChromeの機能であり
Node.jsや別のブラウザでconsole.logに放り込めばそのまま3と表示される可能性はあります。
(Node.jsでは{ [Function: fn] valueOf: [Function] }と表示されて全然駄目ですが)

問題の関数を返し続ける関数の時点でかなりイレギュラーな問題でしたね。
でも頭の体操としては楽しい問題だと思いますし、コードに深みが出る良い問題&模範解答だと思います。

投稿2018/04/26 08:56

編集2018/04/26 09:35
miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Behemoth

2018/04/26 09:14 編集

詳しいご回答をまことにありがとうございます! miyabi-sun様の回答を熟読し、JavaScriptに関する理解を深めたいと思います。 僕の鈍った頭を鍛えないと...
Behemoth

2018/04/26 13:02

一つ質問なのですが(あまりに基本的なことならば、申し訳ありません)、たとえば、add(1)(2)(3)の場合、 1 add(1) 2 add(2) 3 add(3) のように処理が行われているということでしょうか? また、 add(1) でfn(つまり、1)が返されと思うのですが、function(x){ return add(n + x) } の'x'とはどこから持ってくるのですか?
miyabi-sun

2018/04/26 13:21

これは結構わかりにくい中級者〜上級者が意識する概念だったりするので大丈夫ですよ! クロージャーの概念に近い、関数生成時のスコープを利用します。 add(1)を行うとまず引数n=1になります。 そしてfnを生成してvalueOfをくっつけ返します。 戻り値のfn関数は引数xを利用して実行する関数のようです。 ここまでは楽勝ですよね。 実はこの時、引数のn=1は宣言されっぱなしでスコープの中で生き続けます。 続いてadd(1)(2)の解説。 上の結果を元にfn(2)を実行します。 fnの関数はreturn add(n + x)ですよね。 えっ、今叩いたのは2だからx=2は分かるんだけど、nって何なの??? このnの正体は、最初のadd(1)の引数として宣言されっぱなしで放置されていたn=1君なのです。 結果としてfn(2)の戻り値はadd(n + x)からadd(1 + 2)になります。 つまりadd(3)を更に評価して結果を返さなければなりません。 add(3)の実行結果はn=3で宣言しっぱなしにしたままfn関数を作って返します。 念のためadd(1)(2)(3)にいきましょう。 更にfn(3)を実行すると、束縛され続けているn=3とx=3を使いadd(3 + 3)になります。 add(3 + 3)の評価結果は引数n=6を宣言したままのfn関数です。 4回目以降は全てこの繰り返しになります。
Behemoth

2018/04/27 00:53

ご返事が遅くなりました。 理解できました!丁寧なご解説、ありがとうございます。 また、よろしくお願いいたしますm(__)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問