Spells and Charms

プログラミング(呪文学)の学習記録。

クロージャについての理解 | Closures

もし「クロージャって?」と聞かれた時に、「あ、こう答えたら良いかもしれない」という言い方を思いついたので、メモしておきたいと思います。

(言い方って・・) 

 

 

 

その言い方とは、

 

 

関数とその関数が参照できるスコープのセットです

 

 

 

( ・∇・)

 

 

 

関数だけともちがうしスコープだけともちがう、というところがポイントかなと思っています。

 

 

MDNやブログ、クロージャの説明をしている記事など読みまくり、いまのところ・・・・・一番自分がしっくりきている、という考え方になります。

 

 

 

さっそくですが例にうつっていきたいと思います。

 

例1

 

 クロージャはつかっていない例です。

function foo(number){
    let local = 1;
    return local+number;
}
console.log(foo(100)); //101

 

関数fooは実行されると、引数として数字をひとつ受け取ます。

 

そして関数内にローカルでセットされている変数localに対しその数字を足し、
その計算結果を戻り値として返してきます。

 

 

今回はローカル変数をあらかじめ1とセットしました。
そして関数foo実行時に引数として100を渡しました。

 

コンソールに計算結果を表示すると101となります。

 

 

 

 

 

このコードでみていただきたいポイントとしては、

 

変数 local自体を変えることはできないという点です。

 

 

 

( ・∇・)

 

 

 

 

 

例2 

次はクロージャをつかった例です。

function bar(){
    let local = 1 ;    
    return function(number){
        while(local < number){
            ++local;
        }
        return local;
    }
} 

let localToNumber= bar();
console.log(localToNumber(100)); //100

 

関数barを実行しますと関数がかえってきます。

 

その戻り値である関数を実行したいので、変数localToNumberに代入しました。

 

変数localToNumberに代入された関数は、
実行されると、引数として数字をひとつ受け取ります。


そして変数localが受け取った引数よりも小さい間はずっとlocalに1を足しつづけます。

 

変数localが引数より小さいという条件がfalseになった瞬間、whileループ終了。
その時点の変数localが戻り値として返されます。

 

 

 

 

今回はローカル変数として例1と同じく1をセットしました。
今回の戻り値である変数localは、関数barのローカル変数であるにもかかわらず
初期値の1から、戻り値である100まで増加しています

 

 

( ・∇・) 

 

 

 

 

例1では変数localを変更できませんでした。

 

 

 

ここでやっと冒頭で申し上げた、関数とその関数が参照できるスコープのセット のお話がでてきます。

(やっと・・!)

 

 

 

  

関数barの戻り値である関数は、関数barのスコープにあるローカル変数を参照できます。

 

 

 

 

( ・∇・) ?

 

 

 


内側にある関数はその外側スコープに存在している変数をみにいくことができる、と言い換えた方がわかりやすいかもしれません。

 

 

 

 

( ・∇・) !

 

 

 

 

変数localToNumberには、関数だけでなく参照できるスコープも一緒に代入された、と考えるとわかりやすくないでしょうか。

このセットがクロージャだと。

 

f:id:ahochauyo:20190706114308j:plain

汚くてすみません・・



 

 

 

冒頭で申し上げた、関数とその関数が参照できるスコープのセット は、このことです。
 

 

 

 

 

 

暫定的に、「クロージャとは?」と言われた時にイメージするものとしたいと思います。

 

 

 

( ・∇・)!!

 

 

 

 

 

 

実際エンジニアとして活躍されている方々は、もっとわかりやすい例や、クロージャをつかうメリットなど有益な情報を共有されている方もたくさんいらっしゃいます。

 

 

 

自分の場合はまだまだそこまでは到達しておらず、あくまでも「お勉強」という域を出ない中ではあります。

 

 

 

( ・∇・;)

 

 

 

はやく現場でコードに触れてみたいと思う日々ですが、下積みと思って淡々とやっていきたいとおもいます。冗長になりましたがここまで読んでいただきありがとうございます。

コールバックをつかった掛け算タイマー

 

0から100までのランダムな数字を2つ掛け算し、
結果を5秒後に表示するというもの。

 

multiplierはすぐには実行せず、executeの中でコールバックされています。

 

 

JavaScript

function multiplier(num1,num2){
document.getElementById("answer").innerHTML = "The answer:" + num1*num2;
}

function executor(num1,num2){
    document.getElementById("answer").innerHTML = "";
    num1 = Math.round(Math.random()* 100); 
    num2 = Math.round(Math.random()* 100); 
    
    document.getElementById("number1").innerHTML = "Random number1: " + num1;
    document.getElementById("number2").innerHTML = "Random number2: " + num2;

    setTimeout(multiplier, 5000, num1 ,num2);
};

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>set timepout_retry</title>
</head>
<body>
    <h1>Multiply five seconds later</h1>
   <form>       
       <input type="button" value = "start!" onclick="executor();"><br>
       <p id="number1"></p>
       <p id="number2"></p>
       <br><br>
       <p id="answer"></p>
    </form> 
<script type="text/javascript" src="settimeout_retry.js"></script>
<noscript>JavaScript is unavaiable</noscript>
</body>
</html> 

 

 

最初うまく動かなかったのですが、
multiplierをexecuteの引数としてわたしていたのですが、
multiplierが初期化されてしまっていたことが原因でした。

コールバックをつかった掛け算タイマー

 

0から100までのランダムな数字を2つ掛け算し、
結果を5秒後に表示するというもの。

 

multiplierはすぐには実行せず、executeの中でコールバックされています。

 

 

JavaScript

function multiplier(num1,num2){
document.getElementById("answer").innerHTML = "The answer:" + num1*num2;
}

function executor(num1,num2){
    document.getElementById("answer").innerHTML = "";
    num1 = Math.round(Math.random()* 100); 
    num2 = Math.round(Math.random()* 100); 
    
    document.getElementById("number1").innerHTML = "Random number1: " + num1;
    document.getElementById("number2").innerHTML = "Random number2: " + num2;

    setTimeout(multiplier, 5000, num1 ,num2);
};

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>set timepout_retry</title>
</head>
<body>
    <h1>Multiply five seconds later</h1>
   <form>       
       <input type="button" value = "start!" onclick="executor();"><br>
       <p id="number1"></p>
       <p id="number2"></p>
       <br><br>
       <p id="answer"></p>
    </form> 
<script type="text/javascript" src="settimeout_retry.js"></script>
<noscript>JavaScript is unavaiable</noscript>
</body>
</html> 

 

 

最初うまく動かなかったのですが、
multiplierをexecuteの引数としてわたしていたのですが、
multiplierが初期化されてしまっていたことが原因でした。

コールバックと高階関数のちがい/ What is the difference between "callbacks" and "higher-order functions" ?

この動画がわかりやすい。
開始20分あたりから核心にせまるかんじがあり。
コールバックと高階関数のちがいは36分あたりから。
最初からみることで、関数の存在意義を感じやすいと思うので
長いけど最初からみたほうがいいかと思います。

動画中でつかわれているスライドを参照しながらやるとよりわかりやすい。
最初は学校の宣伝で、12ページからスライドスタートです。

 

同じチャンネルで同じプレゼンを別の講師がしているものもありますがこちらがダントツでわかりやすいです。

 

37分30秒くらいから、コールバックと高階関数の説明をしています。
何回もリピートしました。

 

 

コールバック:

functionのparameterとして渡されてきたhogeに入っている関数を実行すること

高階関数:

上を実行する関数

 

ここではhogeがexecuteFunctionで、高階関数がexecute

function plus(x,y){
    return  x+y;
};
function minus(x,y){
    return x-y;
};  
function multiply(x,y){
    return x*y;
};
function divide(x,y){
    return x/y;
};


function calc(x,y,executeFunction){
    return executeFunction(x,y);
};


function execute(x,y,executeFunction){
    x = Number(document.getElementById("number1").value);
    y = Number(document.getElementById("number2") .value);
    console.log("Current x: " + x);
    console.log("Current y: " + y);

    if(document.getElementById("plus").selected){
        executeFunction = plus;
    }else if(document.getElementById("minus").selected){
        executeFunction = minus;
    }else if(document.getElementById("multiply").selected){
        executeFunction = multiply;        
    }else{
        executeFunction = divide;        
    }

    console.log("Current operator: " + executeFunction); 
    console.log("The answer is: " + calc(x,y,executeFunction));
}

 

行いたい計算はexecuteFunctionの中にはいっているのだけど、

コンソール画面を確認すると、

console.log("Current operator: " + executeFunction); は、
この時点では、function definitionでしかないということがわかる。

 

参考までにHTMLはこちら

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>callbacks_retry</title>
</head>
<body>
    <h1>Calculator</h1>
   <form>
       <input type="text" id="number1"><br>
       <input type="text" id="number2"><br>

       <select>
            <option id="plus">+</option> 
            <option id="minus">-</option>
            <option id="multiply">*</option>
            <option id="divide">/</option>
          </select>
       
       <input type="button" value = "calculate!" onclick="execute();"><br>

   </form> 
<script type="text/javascript" src="callbacks_retry.js"></script>
<noscript>JavaScriptが利用できません</noscript>
</body>
</html>






 

高階関数については、エンジニアの友人がおしえてくれたが、
一般的には、「関数を返す関数」のことをさす場合が多いみたいですね。

 

冒頭の動画では、小さな関数たちをmanupilateしているのが、Higher-order Functions だよといっている。

 

このあたりのシナプスがつながるのは、
まだ時間がかかりそう。

 

でも1つだけ、例を理解した。あとはこれを汎用できるようにしていきたいとおもいます。

テクニカルインタビュー 2回目

先日、2回目のテクニカルインタビューをうけました。

前回よりも圧倒的に練習不足を実感。

 

奨学金の申請をしたかったので、直近の締め切りを目標にしていたのですが、
やはり時間が十分にかけられていませんでした。

 

勉強の仕方が良くなかった
単純にかけた時間が少なかった
の2点かなと思います。

 

 

メンターのエンジニアにきいたら、
本を一冊丸ごと完璧にすること!ということだったので、
そうしたいと思います。

 

 

 

コードを書く前に方針をたてよう

方針とは。
インプットはなにか。
アウトプットはなにか。

そして関数の中でなにをするのか。

 

関数でないこともあるかもしれませんが、
要するにどんな処理を経てアウトプットになるのか、書ければいいと思っています。

 

テクニカルインタビューで指摘があってからやり始めたのですが、
すごく役立っている実感があるのでおすすめです。

 

どう役立っている? コメントで方針を書くことのメリット

ここ数日取り組んでみた感想としては、
自覚的にコードを書くのに役立つなあ、というもの。

 

まずコメントで方針を書くだけでイメージが少しふくらみます。
わからん、、としか思わないときと比べて、
何を考えればいいか知っているだけで、だいぶ助けになるなという実感があります。

 

今の私のような入門者レベルの段階では質より量を求めることが大事。
というか将来質を求めたいならば今量をこなすことが大前提、といったほうがいいかもしれないですね。

で、当然量をこなすのには時間がかかるわけですが、
まったく振り返らずに闇雲にやっていると、効率が悪いです。

初期の初期は闇雲でいいとおもいます。
初心者なんですから効率もなにもないです。


ぼんやりとしたイメージですが、
自分が何をしているか自覚しながらコードをかく、ことができるようになってこれば、その先の、コードを書く前からゴールのイメージができるところ、すなわち、立てた方針と書いたコードが同じようなものになってくるところにさらに近づけると思います。

 

具体的にはこんな感じで書いています。 

// input  {“1”: a, “2”:“b”, “3”: “c”}
// output  {“1”: “c”, “2”: “a”, “3”: “b”}
// function inside function declare two variable orgValue and newValue
// using for loop.  

 

最初のうちはコメントはまちがっててもいい。書くのが大事。

ちなみに、コメントはちょっとまちがっていることがほとんどです。
実際のコーディングは細かくデバックしながら進めていくうちにほぼほぼ軌道修正することになります。

たとえば、
変数宣言は、関数の中でなくて外でしないといけなかった。
などそういうことです。

 

これはたぶん、まだ自分のコーディング歴がまだ浅いことが原因だとおもっています。


回数を重ねていくうちに、コーディング前に書くコメントと実際のコードが一致してくることが増えていくのではないかなと思っています。

 

先述のとおりまだまだ実際のコードとは結果的に異なることを書いていることばかりなのですが、今はこれでいいかなと思っています。

 

 

また気づいたことがあったらアップデートします。

ブートキャンプのテクニカルインタビュー その2

30分で私は3つのコードをやりました。
最後は未完成。

 

1つ目はごくシンプルなifを使うコード。

特になにということはなかったと思います。

 

2つ目はすでにあるコードからコンソールになにがでてくるかを当てるもの。

個人的にはこれが一番おもしろいと思いました。


console.logやreturn、関数の呼び出しや変数の宣言といったごく初歩的なコードのみで構成。

関数の呼び出し結果に加えてundefinedがでてくるので、
どうしたらこのundefinedが消えると思う?という質問もされました。

コメントアウトしてどこが作用しているかを見ていく感じでした。
なんとなくふんわり理解しているつもりではだめだなと思わされる問題。

 

次回同様の問題があった時用の、

(というか今後プログラミングする上でと言ってもしれない)心得としては、

「バグ」をみつけるために、
ひとまず、ひとつずつコメントアウトしていくこと。

自分がそこが怪しいと思っていなかったとしても。

 

多分ここだろうな、と目星をつけたところをコメントアウトしたのですが、
変化なしで、その一つしたでした。

自分が手を動かしてやっていたら、
順番に何も気にせずコメントアウトしたと思います。

 

でも今回はペアプログラミングで自分は指示を出すだけでタイプはできないという状況下だったので、いつもどおりが通用しませんでした。

 

シンプルなコードの動きを理解してるかがいかに大事かを考えさせられるという点と、
ひとつひとつの作業に意識的になるという点で面白い問題でした。

 

 

3つ目は時間も間に合わずできませんでした。
でもコードの方向性も特に思い浮かんでいたわけではないので、
これは練習不足。

 

キーバリューの問題をとくようにいわれたので、やります。
キーバリューなどのカテゴリで選べる練習問題のサイトがなかなかないのがネックです。