• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:カリー化について教えてください。)

カリー化についての解説

このQ&Aのポイント
  • カリー化とは、複数の引数を持つ関数を、1つの引数を受け取る関数の連鎖に変換することです。
  • カリー化により、関数の再利用性や柔軟性が向上し、コードの可読性も向上します。
  • カリー化は、関数型プログラミングの一つのテクニックであり、関数をより効果的に使用する方法として重要です。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

>ANo.3補足 書き方は(厳密には)間違ってる気がしますが、理解というか、考えられているとおりだと思います。 理解されてるようなので、先に進んでみます。 var x=5; // 基準値 var add=function(y){return x+y;}; これは基準値(グローバル変数)の5と、add()の引数が加算され、returnされます。 では、基準値を5と10の2種類を計算したい場合はどうすればいいでしょうか。 x=5; answer1=add(10); answer2=add(15); x=10 answer3=add(10); answer3=add(15); そのたびにxを変更すればいいですが、「交互に計算したい場合」に問題が出てきます。 x=5; answer1=add(10); x=10 answer3=add(10); x=5; answer2=add(15); x=10 answer4=add(15); いっそのこと、基準値を引数として渡して、 function add(x,y){return x+y;} の方がはるかにマシですが、毎回、同じ数字を引数にするのも面倒。 ということでクロージャが出てきます。 function closure(arg_x){ var x=arg_x; // これがクロージャ(プライベート変数) return function(y){ return x+y; }; } var C=closure(5); var D=closure(10); answer1=C(10); //5+10 answer3=D(10); // 10+10 answer2=C(15); // 5+15 answer4=D(15); // 10+15 これで、似たような関数を作る必要もなく、交互に計算ができるようになります。 function C(y){ return 5+y; } function D(y){ return 10+y; } 明示的にvar x=arg_xとして変数を生成していますが、たぶん引数のままでもクロージャとして機能すると思います。 さて、足し算だけでなく、引き算をしたいときもあるかもしれません。 そこで、closure()のような関数ではなく、実際の計算をさせるための関数も渡してみます。 function curry(f, x){ return function(y){ return f(x,y); }; } // 実際に計算を行う関数は、基準値も引数として渡す。 // これは関数がクロージャ生成関数の中で定義されているわけではないので、クロージャ変数を使えない。 // また、xはグローバル変数ではないため。 function add(x, y){ return x+y; } function reduce(x, y){ return x-y; } var addTo5=curry(add, 5); //基準値5と足し算 answer1=addTo5(10); var reduceFrom20=curry(reduce, 20); //基準値20から引き算 answer5=reduceFrom20(5); var reduceFrom30=curry(reduce, 30); //基準値30から引き算 answer6=reduceFrom30(5); -------------------- ANo.3でANo.2への返信を書いていますが、 改めて考え直してみると、カリーにするためには関数を渡さなければならないと思います。 ですから、ANo.1で書いたとおり、 「引数のいずれか(通常は第一引数)が関数(関数オブジェクト、関数へのポインタなど)でなければならない」というルールになると思います。 でなければ、単なるクロージャじゃないかと思いますので。 最後のaddTo5(10)はadd(5,10)と同じ reduceFrom20(5), reduceFrom30(5)は、reduce(20,5)、reduce(30,5)と同じです。 個人的にクロージャはよく使う方だと思いますが、カリーは使わないですね。 C++は詳しくないですが、C++の<Template>みたいな感じなんでしょうか? 見直してみるかな。。。

ny_cs
質問者

補足

詳細な解説ありがとうございます。もう1つ伺っておきたいのですが、この関連の問題で、二乗した値を返す関数をCurryを使用して作成せよというのがあるのですが、以下のような式になりますか?? var square = curry(function(x,y){return y * y;}, n); ただし、この場合、Curryの第二引数nは、実際にはどこからも参照されることがないので、なんだか違う気がするのですが、実際に実行してみたところ、二乗の値は返ってきます。いただいたコメントを拝見 をするに、この問題(二乗した値を返す)という内容自体、あまりcurryを使うメリットにはならないように思うのですが。。。意味があって出題されていると思うので、念のため質問させていただく次第です。 いろいろすみませんが、アドバイスをいただけると光栄です。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (4)

回答No.5

> 二乗した値を返す関数をCurryを使用して作成せよというのがあるのですが、以下のような式になりますか?? たぶん、問題に問題があると思います。 二乗するだけなら、カリーどころかクロージャも不要です。 var square=function(n){return n*n;}; var ans=square(5); > var square = curry(function(x,y){return y * y;}, n); 問題そのものが悪いので、これでも良いと思いますが、 > ただし、この場合、Curryの第二引数nは、実際にはどこからも参照されることがないので、なんだか違う気がするのですが、 そう思われるなら正しいと思います。 var square = curry(function(n){return n * n;}, n); としておけば、 var square=curry(function(n){return n * n;}, 5); var ans=square(); squareの呼び出しは引数無しで実行できるようになります。 使いどころは、、、ありません。 常に同じ値を返す関数はグローバル変数の替わりにできそうですが、CPUやメモリを使う無駄が大きすぎます。 よほど特別な理由がない限り、関数にする必要はないと思います。 まぁ、例題としてなら、実用性は関係ないと思いますけどね。 学校?ではこんなのもやってるんですねぇ。

ny_cs
質問者

補足

>まぁ、例題としてなら、実用性は関係ないと思いますけどね。 その後、こんなコードも考えてみました。この方法だと、べき乗の指数をcurryの第二引数に指定できるので、多少は意味があるのではないかと思い直しまして。。。 function curry(f, x) { return function(y) { return f(y, x); <- xとyを入れ替えて } } var square = curry(Math.pow, 2); ←二乗のケース square(5)=25 となる。 var cube = curry(Math.pow, 3); ←三乗のケース cube(5)=125 となる。 >学校?ではこんなのもやってるんですねぇ。 そうなんですよ。しかも、大学院(in NY)の授業です。英語で授業を受けていて、教科書も英語で。。。という状況だったので、コメントいただけて、本当に助かりました。クラスメイトの大半は、インド人で独特のなまりのある英語を話すので、クラスメイトとのコミュニケーションも難しくて。。。いろいろな点で大変です。 本当にありがとうございました!!

全文を見る
すると、全ての回答が全文表示されます。
回答No.3

サンプルとして提示されたcurry()には、f(x,y)という部分がありますので、 このコードがエラーにならずに実行されるためには、fは関数である必要がありますので、ANo.1ではの質問1ではYESと答えましたが、 > ANo.2 つまり引数に関数を渡す必要がなく、 curry()の中で、(returnのための無名関数とは別の、引数を処理するための)無名関数を生成してもよいという前提のもとであれば、 「一般論としてcurry()の第一引数が関数である」という命題に対してはNOになります。 そのため、質問2の回答も異なってきます。 > ANo.1補足 > result=C(10)は、どこから登場してきたのでしょうか。 これ自体は検証用ですので、なんでもかまいません。 よくわからなければ消してください。 > curryの中の関数を呼び出すには、c()を使うことになるというくだりが、すみません、本当によくわかりません。。。 クロージャとかカリー化とかスコープとかの問題ではなく、 「関数の引数と戻り値とはどういうものか」、だけで話をすすめます。 (例) var C=function(y){f(x,y);}; function C(y){f(x,y);} この2つの違いはわかりますか? (例2) function add(a, b){ return a+b; } var result=add(5, 10); この2行は何をやっているのかわかりますか? これがわからなければ先に進めませんので、 すみませんが、クロージャとかカリー化のことは忘れて、基本を勉強し直してください。 では、resultだけで書き直します。(ANo.2と同じになりますが) function curry(f,x){ return function(y){ f(x,y); }; } var result=curry(add, 5); function curry()を実行したら、何がreturnされ、resultには何が入るでしょうか。

ny_cs
質問者

補足

(例) var C=function(y){f(x,y);}; function C(y){f(x,y);} この記述でわかりました!つまり、関数curryは、function literalをreturnしているのですね。 つまり、最初の回答で、以下の例題を出してくださっていたのは、C(y)=curry(add,5)=add(5,y)ということだったのですね。なので、C(10)=add(5,10)=15となる、ということですよね?? 個人的にはこの流れでストンと腑に落ちたのですが、この理解であっていますか?自信がないので、念のため確認させてください。 (最初に出してくださって例題) var C=curry(add,5); var result=C(10);

全文を見る
すると、全ての回答が全文表示されます。
  • auty
  • ベストアンサー率58% (284/486)
回答No.2

・ カリー化という言葉を始めて聴き、以下のURLで勉強させていただきました。 http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%BC%E5%8C%96 カリー化(currying)とは、複数の引数をとる関数を、 引数が「もとの関数の最初の引数」で、 戻り値が「もとの関数の残りの引数を取り結果を返す関数」 であるような関数にすること。 ・ これによると、1.は NO    第二引数xがあるので、サンプルコードのcurry(f, x)は、カリー化されていません。    したがって、2.も飛ばします。(話がカリー化でなければYES)    3.は、以下のコードを参考にしてみてください。    4.に関しては、変数や関数の有効範囲をチェックして見ましょう。      この中に、Callオブジェクト、スコープチェーンやクロージャの話が出てきます。 ------------------------------------------------------------ add関数を定義したときの 3.の検討。    curry(add,1)は、関数の定義自体か返されます。つまりその関数を実行するわけではありません。    yには、自分で値を設定する必要があります。 ------------------------------------------------------------ <html> <head> <script type="text/javascript"> <!-- function add(x, y) { return x + y; } function curry(f, x) { return function(y) { return f(x, y); } } function curry_disp() { var y = 1; var ss1 = "curry(add,1) = " + curry(add,1); var f = curry(add,1); var ss2 = "f(3) = " + f(3); var disp = document.getElementById("disp"); disp.innerHTML = ss1 + "<hr />" +ss2; } //--> </script> </head> <body> <button type="button" onclick="curry_disp();" />下に表示</button> <hr /> <div id="disp"></div> </body> </html> ------------------------------------------------------------ また、以下のURLの内容をブラウザで確かめてみました。 http://d.hatena.ne.jp/m-hiyama/20051213/1134446855 ------------------------------------------------------------ <html> <head> <script type="text/javascript"> <!-- function sum(x, y) { return x + y; } function curried_sum(x) { return function (y) {return sum(x, y);} } function curry() { alert(curried_sum(1)(3)); var f = curried_sum(1); alert( f(3) ); } function curry_disp() { var ss1 = "curried_sum(1)(3) = " + curried_sum(1)(3); var f = curried_sum(1); var ss2 = "f(3) = " + f(3); var disp = document.getElementById("disp"); disp.innerHTML = ss1 + "<hr />" +ss2; } //--> </script> </head> <body> <button type="button" onclick="curry();" />alert</button> <button type="button" onclick="curry_disp();" />下に表示</button> <hr /> <div id="disp"></div> </body> </html>

全文を見る
すると、全ての回答が全文表示されます。
回答No.1

このコード、どこかで見たことがありますが、、、どこだったかな。。。 > 1.curry(f,x)の第一引数fは、関数で、第二引数xは変数と理解してよいのでしょうか? YES > 2.カリー化により、最終的に返される値f(x,y)というのは、curry関数が呼び出された際の第一引数の関数f()に、curr関数の第二引数と、function式を用いて直接宣言されたyを渡した結果が返されるということでしょうか? YES 3と一緒に回答します。 > 3.仮に、curry(add,1)と呼び出した場合には、yにはどのような値が入るのでしょうか? curry呼び出しの時点では不明。 説明しづらい、、、コードレビューでいいですか? function curry(f, x) { return function(y) { return f(x, y); } } function add(a,b){ return a+b; } var C=curry(add, 5); var result=C(10); // C生成時のcurryの第二引数 5 とC()の第一引数 10 がaddにより計算される alert(result); //15 var result=C(15); // C生成時のcurryの第二引数 5 とC()の第一引数 15 がaddにより計算される alert(result); //20 var D=curry(add, 10); var result=D(10); // D生成時のcurryの第二引数 10 とD()の第一引数 10 がaddにより計算される alert(result); //20 var result=D(15); // D生成時のcurryの第二引数 10 とD()の第一引数 15 がaddにより計算される alert(result); //25 > 4.その他、カリー化を学ぶ上で重要なことがありましたら教えてください。 「クロージャ」です。 上記では、curryの中でfとxがクロージャになっています。 変数yはcurryの中に入っていますが、実際には function f(y){}の引数のことですので、fを呼び出さない限り、yの値は確定しません。 で、このfというのはcurryの戻り値として変数に入りますので、 最終的には C=function(y){} と同じになります。 curryの中の関数fを呼び出すには、C()を使うことになります。

ny_cs
質問者

補足

詳細な解説ありがとうございます。ただ、当方の知識不足ということもあり、以下の部分からすでによくわからないのですが。。。。result=C(10)は、どこから登場してきたのでしょうか。curryの中の関数を呼び出すには、c()を使うことになるというくだりが、すみません、本当によくわかりません。。。教えてください。 var C=curry(add, 5); var result=C(10); // C生成時のcurryの第二引数 5 とC()の第一引数 10 がaddにより計算される alert(result); //15

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • Javaスクリプトの初歩の初歩

    Javasript覚えがてらに以下の カリー化されたソースを実行しましたが、Type Error:add is not functioという 例外が返ってきます。なぜ、ファンクションエラーになるのか教えてください <html> <head> <meta http-equiv="content-type" content="text/html;charset=UTF-8"> <script src="jquery-1.9.0.min.js"> </script> <script> <!-- function add(x,y){ var oldx=x,oldy=y; if(typeof oldy==="underfined"){ return function(newy){ return oldx+newy; }; } return x+y; } typeof add(5); add(3)(4); var add2000=add(2000); add2000(10); --> </script> </head> <body> </body> </html>

  • C言語(引数)

    はじめまして。 C言語を習い始めたものです。 関数を定義するとき、よく耳にする、仮引数や実引数があると思います。 仮引数は関数の定義内で値をうけとる変数のことであり 実引数は関数を呼び出す際に渡す値を実引数というらしいのですが どこからどこまでを仮引数と呼ぶのかわかりません。 例えば、 fの関数の定義内で ↓があるとします。 (関数にする意味はないのですが、確認のためあしからず・・・) double f(double x) {     x=5;     return(x); } この場合、仮引数とよばれるものは double f(double x)の xが仮引数であって x=5;のxは仮引数と呼ばないのでしょうか?? もしそうならば void f(double x) { printf("%f",x); } のprintf("%f",x);内のxは仮引数とよぶのでしょうか? 質問の内容が意味不明かもしれませんが よろしくお願いします。

  • 関数の戻り値に関数のアドレスを返すできませんか?

    戻り値に関数のアドレスを与える方法が良く分かりません. ひとまず,以下のように動くプログラムを作りたいと思っています.  f2(1,2)(1); //このように引数の()を二種類に分けたいのですが 無理でしょうか?? プログラムは下のように作って実験しているのですが良く分かりません・・ どなたか分かる方居たら教えてください. int f1(int x , int y){ return 0; } ????? int ff(int x){ return f; };

  • Rにおいて

    Rにおいて f3 <- function(x) { return(pweibull(1,shape=1,scale=x)) } という関数はx=1において微分可能ですか。可能ならば、その値はいくつになりますか。

  • お願いします

    int c(int x)    { x = x + 3;     { int t; t = 6;      { int x; x = 6; t = x + t + 12; }    return x + t;    } } 上の関数の変数の名前替えだけが許されるという条件の下で,関数f の働き(すなわち,引数と戻り値の関係)を変えずに,上記の関数定義の4行目 { int x; x = 6; t = x + t + 12; } の(int x)を(int t) に変更して.必要最小限の変数の名前替えを施した関数定義を書け. 私はこのように定義していますので、正しいですか、「関数の変数の名前替えだけが許されるという条件の下で,関数f の働き(すなわち,引数と戻り値の関係)を変えずに」ってあまり理解できてないので、ご教授お願いいたします。 int c(int x)    { x = x + 3;     { int y; y = 6;      { int t; t = 6; y = t + y + 12; }    return x + y;    } } }

  • c言語の関数定義について

    次の関数定義を考える. int f(int x) {if (x > 0) {return x * f(x-1);} else {return 1;} } この関数f と働き(すなわち,引数と戻り値の関係)が同じで再帰呼出(recursive call) を使わない関数g をC で定義せよ.ただし,オーバーフロー(overflow) については考慮しなくてよい. ”この関数f と働き(すなわち,引数と戻り値の関係)が同じで再帰呼出(recursive call) を使わない関数g をC で定義せよ”って理解できません、どのように定義したいいか、ご教授お願いします。

  • こんな条件を満たす乱数生成関数教えてください

    1.任意の周期を指定できる 2.種を指定できる(直前の生成値を引数にとる) 3.逆関数が定義できる 4.生成された乱数 x、y の距離を(定数時間で)求められる   つまり y = f(x) ならxとyの距離は1、 y = f( f( f(x) ) ) なら距離3、というように 乱数としての質(均等に分布していること)はあまり重視しません。 ビット幅は32~128bitくらい(任意ならベスト)であればいいと思っています。 以下のような感じにしたいです。  int rand(x, p);   // 戻り値 y = f(x)、pは周期、xは直前の乱数値  int inv(y, p);   // 戻り値 x = f^-1(y)  int distance(x, y) // y = f(f(x)) のとき、distance(x, y) = 2 で distance(y, x) = -2 一応以下の関数が条件1~4を満たすのですが、残念ながら乱数としての性質が皆無なので使えないです。  int rand(x, p) { return (x+1) % p; }  int inv(y, p) { return y ? y - 1 : p-1; }  int distance(x, y) { return y - x; } よろしくご教授お願いします。

  • C言語の実数型の足し算

    C言語初心者です。関数の勉強していて、実数型計算に出くわしました。 #include <stdio.h> float add(float a, float b) { return a+b; } int main(void) { float x=10.5,y=20.3; printf("%f %f\n",x,y); printf("%f\n",add(x,y)); return 0; } としたら、 10.500000 20.299999 30.799999 という結果になりました。今のところint型でずーっと勉強していたので、20.3の20.299999表記が怪しく感じられ、結果も同様に怪しく感じられます。どうして、10.5+20.3=30.8とすっっきり表示してくれないのでしょうか。

  • Matlabで自作関数をオーバーロード

    Matlabの組み込み関数の中には、引数の数や戻り値の数に応じて異なる作用をするものが沢山あると思います。 例えば組み込みのmax関数だと y=max(x) ならxの第一次元にについての最大値を返しますし、 [y,i]=max(x) なら、さらに i に最大値のindexを代入、 一方 x=max(x,y) ならx,yの各要素について大きい方を取った行列を、 x=max(x,[],d) ならxのd次元についての最大化をしてくれます。 質問ですが、そういう関数を自分で作ることは可能ですか? function [z,w]=f(x,y) という風に関数を定義してしまうと、たとえyを関数中で利用しない場合でも、引数yを指定しなければエラーになってしまいます。同じ関数名でも呼び方に応じて異なった操作をさせることはできないでしょうか?

  • 累積分布関数

    ランダムベクトル(random vector) (X,Y)の結合密度関数(probability density function)が以下で与えられています。 f(x,y)=c( x^{2} - y^{2} )e^{-x} -x≦y≦x,0<x<∞ =0(その他) X=xで与えられるYの累積分布関数(conditional distribution function)を示してください。