Buno Journals

It's what I do that defines me.

JavaScriptのthisとは何か

開眼!  JavaScript ―言語仕様から学ぶJavaScriptの本質

開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質

thisとは

もっとも基本的な考え方は、

thisとは、関数を呼び出すオブジェクトへのリンク。 thisとは、関数のスコープ内で有効な値で、実行中の関数をプロパティまたはメソッドとして保持しているオブジェクトへの参照である。

よって、実際にthisが何を指しているかは、関数定義時ではなく、実行時のコンテキストに依存する

// このfooはグローバルオブジェクトのスコープで定義
 var foo = 'I am foo of global scope';

// このfooはmyObjectのプロパティ
var myObject = { foo: 'I am myObject.foo' }

var sayFoo = function() {
  console.log(this.foo);
};

myObject.sayFoo = sayFoo;

// グローバルスコープから実行
sayFoo(); // 出力: 'I am foo of global scope', thisはグローバルオブジェクト

// myObjectのメソッドとして実行
myObject.sayFoo(); // 出力: 'I am myObject.foo', thisはmyObject

※ 私が確認したところJSFiddleでは上記が一部期待通りに動かない。以下の動作が原因のようだが。

var foo = 'foo';
console.log(this.foo); // JSFiddle上では、出力: undefined

call()やapply()で実行する場合

function.call(object, . . . )function.apply(object, [. . .])の形でfunctionを実行した場合、 関数の呼び出し元は第一引数のobjectになる。 つまり、関数内のthisが参照するのは、このobjectだ。

call()やapply()で関数を呼び出すと、その関数内のthisが指すオブジェクトを自分で設定することができる。

var myObject = {};

var myFunction = function(param1, param2) {
  this.foo = param1;
  this.bar = param2;
  console.log(this);
}

// 出力: Window {..., foo: "foo", bar: "bar"…}
myFunction('foo', 'bar');

// 出力: Object {foo: "foo", bar: "bar"}
myFunction.call(myObject, 'foo', 'bar');
myFunction.apply(myObject, ['foo', 'bar']);

入れ子関数の中のthis

二つ以上の関数を入れ子にした場合、前述の基本に反した動作をするので注意が必要だ。

入れ子関数の中のthisは、グローバルオブジェクトを参照する。

var myObject = {
  func1: function() {
    console.log(this); // 出力: myObject
    var func2 = function() {
      console.log(this); // 出力: window(グローバルオブジェクト)
    }();
  }
};

myObject.func1();

この仕様はECMAScript5で修正されるという記載があったが、 現時点Chromeで確認してもこの通りだっった。

OSX El Capitan 10.11.3, Google Chrome 51.0.2704.84 (Official Build) (64 ビット) で確認

コンストラクタ関数の場合

コンストラクタ関数内のthisが何を指すかは、new演算子を使って実行したか否かで変わる。

var Person = function(name) {
  this.name = name
}

// newあり。インスタンス生成
var bonnie = new Person('Bonnie Parker');
console.log(bonnie); // 出力: Person {name: "Bonnie Parker"}

// new無し。ただの関数実行
var clyde = Person('Clyde Barrow');
console.log(clyde); // 出力: undefined
console.log(window.name); // 出力: Clyde Barrow

newでコンストラクタを実行した場合、インスタンスが生成される。 そして、コンストラクタ関数内のthisはこの生成されたインスタンスである。

new無しの場合、ただの関数呼び出しであるので、上記の場合だとthisはグローバルオブジェクト(window)になる。

参考

開眼!  JavaScript ―言語仕様から学ぶJavaScriptの本質

開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質

developer.mozilla.org

qiita.com

AtomにEmmetを入れて効かなくなったキーバインディングを解決する

Emmet for Atom

Emmet — the essential toolkit for web-developers

Emmetというツールがある。

htmlやcssを独自の省略記法で入力(してから展開)できるのでコーディングが早くなる。

このEmmetをAtomで利用するパッケージがあるが、

atom.io

これをインストールすると、既存のキーバインディングが一部上書きされて効かなくなってしまう。

Atomのキーバインディング修正

まず、気付いたのは

  • Ctrl + e : 行末へ移動
  • Ctrl + d : カーソル次の文字削除

が効かなくなっていることだ。

Atomキーバインディングの修正は、

Settings(Cmd + ,) > Keybindings > your keymap file > keymap.cson

このkeymap.csonファイルを修正することで行う。(ファイルの場所は~/.atom/keymap.cson

You can override these keybindings by copying and pasting them into your keymap file

github.com

で、報告されている通り

#workaround for emmet mapping of expand-abbreviation
'.editor:not(.mini)':
  'ctrl-e': 'unset!'                           # remove all bindings
  'ctrl-e': 'editor:move-to-end-of-line'       # remap core binding
  'alt-cmd-e' : 'emmet:expand-abbreviation'    # alternate binding for emmet

と記載すればCtrl - eで行末に移動できるようになる。

Ctrl - dの方は

'.platform-darwin atom-text-editor:not([mini])':
  'ctrl-d': 'unset!'

で次の文字削除ができるようになった。(私はOS X環境である)

参考

HTML/CSSを爆速コーディング Emmet入門 第1回 Emmetを薦める理由 | Adobe Creative Station

HTML や CSS を書くのがめっちゃ捗る Emmet を Atom でも使う | Lonely Mobiler

Keybinding ctrl-e (emmet:expand-abbreviation) interferes with core keybinding · Issue #119 · emmetio/emmet-atom · GitHub

JSFiddleで外部jsライブラリを読み込む

開眼!  JavaScript ―言語仕様から学ぶJavaScriptの本質

開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質

これまで、JSは対応が必要になったら使う程度で、じっくり学習したことがなかったので、 書籍で少しずつ勉強している。

JSFiddle

jsfiddle.net

まず、この便利な実行環境のサービスをを知らなかった。 詳しくは 以下で。

kachibito.net

Underscore.js

書籍の中でUnderscore.jsが紹介されている。

Underscore.js

github.com

underscore.js または、圧縮された underscore-min.js を読み込んで利用する。

この説明も外部リソースに譲る。

http://dotinstall.com/lessons/basic_underscorejsdotinstall.com

JSFiddleで外部jsライブラリを読み込む

正当なやり方は、JSFiddleの画面左側にある"External Resources"から行う。

f:id:bunoacts:20160616132715p:plain

ここをクリックするとフォームが現れるので、ライブラリのURIを入力して+マークを押せば設定完了だ。

適切なCDNを利用する

外部ライブラリURIとして、https://raw.githubusercontent.com/jashkenas/underscore/master/underscore-min.js のような、github上のrawファイルを指定してはいけない。

f:id:bunoacts:20160616133856p:plain

f:id:bunoacts:20160616133932p:plain

Refused to execute script from 'https://raw.githubusercontent.com/jashkenas/underscore/master/underscore-min.js' because its MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.

githubはCDNではないというアラートが表示される。実際、 MINEタイプが 'text/plain' となり、jsのライブラリとして実行することができない。

単純に動作を確認したいのであれば、cdnjsでホストされているURIを指定したり、

cdnjs.com

RawGitというサービスで、githubから変換されたURIを得ることができるので、これを指定する。

rawgit.com

上記サービスは、本番環境での利用は非推奨なので、そいう場合は自前でCDNを用意しなければいけない。

追記:Google Hosted Libraries

developers.google.com

jQuery等、有名なライブラリはGoogleがホストしているので、ここから読み込むのが良さそうだ。

参考情報

githubのjavascriptやcssをrawgit使ってCDN経由ぽく読み込んで使ってみるテスト for jsfiddle - tweeeetyのぶろぐ的めも

CDN代わりにgithubを使いたい場合は外部サービスrawgithubを利用しよう - Qiita

Rubyでマージソート

bunoacts.hatenablog.com

クイックソートは、元データの並び方が悪いと、ソートの効率がバブルソートと大差がなくなってしまう(最悪時間計算量が O(n2)

こんな場合、より効率的にソートできるアルゴリズムマージソートである。

マージソートの流れを簡単に説明すると、

  1. 全体を小さなブロックに区切る
    1. 全体を2つに分ける
    2. 分けた2つを、さらにそれぞれ2つに分ける
    3. ブロックの大きさが1になるまで分け続ける
  2. 各ブロック同士をソートしながらつなぎ合わせる(マージ)
  3. 全体がソート済みになったら完了

f:id:bunoacts:20160527114339g:plain

WikipediaRubyの実装例があるので、コメント追記しながら処理を追ってみる。

マージソート - Wikipedia

def mergesort lst
    _mergesort_ lst.dup  # 副作用で配列が壊れるので、複製を渡す
end

def _mergesort_ lst
    return lst if (len = lst.size) <= 1

    # pop メソッドの返す値と副作用の両方を利用して、lst を二分する
    lst2 = lst.pop(len >> 1)

    _merge_(_mergesort_(lst), _mergesort_(lst2))
end

# 二つの配列をソートしてつなぎ合わせる(マージ)
def _merge_ lst1, lst2
    len1, len2 = lst1.size, lst2.size
    result = Array.new(len1 + len2)
    a, b = lst1[0], lst2[0]
    # i: マージ後の配列resultのインデックス
    # j: lst1のインデックス
    # k: lst2のインデックス
    i, j, k = 0, 0, 0
    loop {
        if a <= b
            result[i] = a
            i += 1 ; j += 1
            break unless j < len1
            a = lst1[j]
        else
            result[i] = b
            i += 1 ; k += 1
            break unless k < len2
            b = lst2[k]
        end
    }
    while j < len1 do
        result[i] = lst1[j]
        i += 1 ; j += 1
    end
    while k < len2 do
        result[i] = lst2[k]
        i += 1 ; k += 1
    end
    result
end

実行例

[2] pry(main)> ary = ('a'..'z').to_a.shuffle
=> ["h", "w", "y", "j", "c", "f", "g", "o", "p", "b", "q", "s", "e", "d", "a", "i", "z", "k", "n", "v", "x", "m", "u", "r", "l", "t"]
[3] pry(main)> mergesort(ary)
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

参考書籍

プログラミングの宝箱 アルゴリズムとデータ構造 第2版

プログラミングの宝箱 アルゴリズムとデータ構造 第2版

問題を早く解くために検討すること

paizaというWebサービスがある。

ITエンジニアの転職情報サービスなのだが、出題された課題を制限時間内に解くコーディングスキルチェック機能もある。

paiza.jp

純粋に問題を解くのが面白くて、少しやってみたのだが、

制限時間を与えられると、如何に効率的に早く解けるかを考えるようになる。

そこで気づいたことを以下に書いてみる。

問題を分解する

複雑で一筋縄ではいかない問題を考え続けていると、時間は経っているのに思うように進めていない時がある。

そんな時は、一度冷静になって問題を分解できないか考えてみる。

ひとかたまりに見える厄介な問題を、普通に対処できる問題の集合に分解できないだろうか。

プログラマなら関数を定義する。

対処できる問題の一つを全体から外に切り出す。

これで全体からは関数の中の詳細が見えなくなる。

適切に抽象化さていけば、問題全体は遥かにスッキリして、分かりやすい秩序が見えてくる。

さらに、解く者にとっても、小さいながらも確実に前進している実感がある。大きい問題の前に足踏みしていたような停滞感は軽減されるはずだ。

思えばプログラマに限った話ではない。

バガボンド』で、宮本武蔵一乗寺下り松で吉岡一門70名と斬り合うが、

ここで武蔵が考えたのは、 1対1の決闘を流れるように70回繰り返す、というやり方だった。(逆に吉岡側は1対70の決闘を一遍にやりたい)

f:id:bunoacts:20160525144311p:plain

バガボンド(26)(モーニングKC)

バガボンド(26)(モーニングKC)

話が逸れたば、何かに時間がかかってしまっている時は、問題を切り分けるというやり方を検討したい。

急いで局所的な最適化をしない

私がコーディングする時の流れは大雑把に言うと以下だ。

  1. 期待通り動くように書く

  2. よりシンプルに読みやすくなるように推敲する

推敲すること自体は大切な習慣だ。「動く」だけのコードをコミットされても困る。当然リファクタリングの余地がないか検討してみる。

しかし、私は時々、まだ問題全体が解けていないにもかかわらず、ある一行を推敲するために時間を使っている自分に気づくことがある。

なかなか定量的な評価は難しいが、この早い段階で局所的な最適化にかける時間があまり効果的ではないように感じられるのだ。

つまり、局所的には完璧でなくとも十分役立つコードを組み合わせて問題全体を解く => リファクタリング

という流れの方が完了まで早く辿り着けるのではないだろうか。

そういえば、37signalsの書籍『小さなチーム、大きな仕事』に似た記述があったので引用する。

建築家は、階の設計が終わるまではシャワーにどのタイルを使うか、また台所にどのメーカーの食器洗浄機を置くかなんて気にしない。 そうした細かなことは後で決めた方がいいとわかっているのだ。...(中略)

問題ではないところで気が取られてしまい、結局は変化していくようなことに対する決断に無駄な時間を費やすことになるのだ。 しばらくの間は細かいことは気にしないことだ。まず根本的なところを固めて、特殊なところは後で考えればいい。

小さなチーム、大きな仕事〔完全版〕: 37シグナルズ成功の法則

小さなチーム、大きな仕事〔完全版〕: 37シグナルズ成功の法則

  • 作者: ジェイソン・フリード,デイヴィッド・ハイネマイヤー・ハンソン,黒沢 健二,松永 肇一,美谷 広海,祐佳 ヤング
  • 出版社/メーカー: 早川書房
  • 発売日: 2012/01/11
  • メディア: 単行本
  • 購入: 21人 クリック: 325回
  • この商品を含むブログ (36件) を見る

以上は、特に目新しい発見ではないが、ついつい頭から抜け落ちている時がある。

これらが強く意識されたのは制限時間が与えられたり、問題を解くのにかかった時間が計測されて明確に表示されるからだろう。

つまり、普段からポモドーロテクニックなりで工夫して、どの課題にどれだけ時間をかけたか明確に分かるようにしておけば、 早く解くための工夫、効率化は自然に生まれてくるのだと思う。 自分の時間管理に活かしたい。

アジャイルな時間管理術 ポモドーロテクニック入門

アジャイルな時間管理術 ポモドーロテクニック入門

アメリカの大学院でコンピュータサイエンスを学ぶためのPrerequisites

私は日本の大学工学部を卒業してプログラマになったのだが、専攻が情報工学系ではなかったので

コンピュータサイエンスをきちんと体系的に学びたい」

という気持ちがあった。

仕事で必要になった内容を、その都度勉強して来たものの、「コンピュータサイエンスの基礎で理解が不十分な部分があるんだろうなあ」と感じていたからだ。

加えて、昔からアメリカに行きたいという気持ちもあったので、「アメリカの大学院に入学してコンピュータサイエンスを専攻するにはどうすればいいか」を調べてみた。

学費、GPA、推薦状、TOEFLGRE等はコンピュータサイエンスに限らず、アメリカ留学で一般的なことなので今回は触れない。

少し自力で調べた後、専門家である栄陽子さんのところへ相談に行ってみた。

www.ryugaku.com

海外留学の情報はWeb上に溢れていて玉石混交と思うが、

栄陽子さんは留学関連の書籍を数多く書かれていて、読んでみると信頼できそうな方だと感じたからだ。

留学・アメリカ大学への道 新版

留学・アメリカ大学への道 新版

まず言われたのは「大学院に進む前に修めておくべきPrerequisites」だった。

「以下のような科目の単位を大学で取っておいてね」ということだ。(もちろん高GPAが望ましい)

Prerequisitesは当然あるだろうと思っていたものの、どの科目かは自分の中で明確になっていなかった。 具体的な科目リストを出して頂いたことで、整理できて助かった。

そして、「働きながら、これらの科目に相当する単位を取得するには、どの大学が適切か?」について調べてみたのだが、

これについては、また別の記事で書くことにする。

C言語とRubyのクイックソート

クイックソートの流れを簡単に説明すると

  1. ある適当な値(文字・数)を決めて、それよりも大きいものは後ろへ、小さなものは前へ移動する。
  2. 2つに分けたそれぞれのグループの中で、また適当な値を決めて、それよりも大きいものは後ろへ、小さなものは前へ移動する。
  3. またそれぞれの...、と1,2の操作を繰り返す。
  4. それ以上ブループ分けできなくなったら終了。

本書に載っているC言語による実装を引用する(処理の意図が分かりやすいように、一部コメントは追記した)。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 10 // データ件数

int sort[N];

void QuickSort(int bottom, int top, int *data) {
    int lower, upper, div, temp;
    if (bottom >= top) {
        return;
    }

    // 先頭の値を「適当な値」とする (これをピボット、と言う)
    div = data[bottom];

    for (lower = bottom, upper = top; lower < upper;) {
        while (lower <= upper && data[lower] <= div) {
            // ピボットより大きい値を左から探す
            lower++;
        }
        while (lower <= upper && data[upper] > div) {
            // ピボット以下の値を右から探す
            upper--;
        }
        if (lower < upper) {
            // 見つかった値を入れ替え(ピボット以下は前、ピボットより大きい値は後の方へ集まる)
            temp = data[lower];
            data[lower] = data[upper];
            data[upper] = temp;
        }
    }
    // 最初に選択した値を中央に移動する
    temp = data[bottom];
    data[bottom] = data[upper];
    data[upper] = temp;

    // ピボットより前部分と後部分を同様にソート
    QuickSort(bottom, upper - 1, data);
    QuickSort(upper + 1, top, data);
}

int main(void) {
    int i;

    srand((unsigned int)time(NULL));

    printf("ソート準備\n");
    for (i = 0; i < N; i++) {
        // ランダムな値を配列に格納
        sort[i] = rand();
        printf("%d ", sort[i]);
    }

    printf("\nソート開始\n");
    QuickSort(0, N - 1, sort);

    for (i = 0; i < N; i++) {
        printf("%d ", sort[i]);
    }
    printf("\nソート終了\n");

    return EXIT_SUCCESS;
}

正直、初見では処理を追って理解するのに少し時間がかかった。

クイックソートRubyで書くとどうなるのか。

class Array
  def quick_sort
    return self if self.length <= 1
    pivot = shift
    left, right = partition { |e| e < pivot }
    left.quick_sort + [pivot] + right.quick_sort
  end
end

C言語よりも遥かに処理を理解しやすい。人間の頭に優しい。

[4] pry(main)> ary = ('a'..'z').to_a.shuffle
=> ["r", "b", "u", "d", "h", "s", "m", "x", "e", "i", "k", "a", "j", "v", "o", "t", "z", "n", "c", "f", "w", "g", "y", "q", "l", "p"]
[5] pry(main)> ary.quick_sort
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

参考:

Rubyでクイックソート - Qiita