2017年 08月の投稿を表示しています

Chrome の console.log で CSS プロパティが指定できるよ

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

以前からごみばこいんでは、コンソールにようこそメッセージを出していました。

Chrome Devtools のドキュメントを見ると console.log の詳細が書かれています。

診断とコンソールへのログ出力  |  Web  |  Google Developers

ドキュメントには CSS プロパティが利用できるとあるのですが、なぜかこれまでは色とサイズが変えられる程度だと思っていました。試すと text-shadow が出来たり border が出来たりと、様々なプロパティが効くようです。

var properties1 = [
    'font-size: 8em;',
    'color: white;',
    'padding: 5px;',
    'background: linear-gradient(red, orange, yellow, green, aqua, blue, violet);',
    'text-shadow: white 0 0 15px;'
];

var properties2 = [
    'font-size: 8em;',
    'color: #222;',
    'text-shadow: white 0 0 3px;',
    'margin-left: calc(-15em - 6px);',
];
console.log(
    '%cうぇるかむとぅーごみばこいん!%cうぇるかむとぅーごみばこいん!',
    properties1.join(''),
    properties2.join('')
);

いくつかプロパティを試してみたのですが display だったり float だったり position 、 top や left あたりは無効化されているようです。。

で。実は Inspector を使うと、ここも HTML/JS/CSS で構成されていることがわかります。
Inspector は URL バーに chrome://inspect/ と入力するとアクセス出来ます。その中でも Other の項目を見ると Developer Tools について Inspect することができます。

DevTools の DevTools ができるなんてちょっと不思議な光景ですねw

こうやって console.log した結果を眺めてみると %c で指定したものは span タグの style 属性に入っています。
これでは :beforeなどのセレクタが使えなかったり、階層構造が持てないのでアニメーションしたりなどなども出来ないです。先にあった使えないプロパティのところもあるので、凝ったことをするのはできなさそうですねー。残念。

MySQL の文字コードについてチョット調べた

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

MySQL 使ってて utf8_unicode_ci や utf8_general_ci とか mb4 …? とプチ事故が起きたので解消するべく調べた。

照合順序とは

「utf8_general_ci」といった文字コードの指定してるっぽいやつは、文字列カラムにおけるコレーション(=照合順序)と呼ばれる。ここで指定された値によって、MySQL が文字列を解釈したり比較を行うようになっている。

表記は 文字コード_文字セット名_比較方法(※特殊なものとして *_bin もある)

  • 文字コード
    • utf8mb4 とか utf8 とか cp932 とか
  • 言文字セット名
    • japanese とか german とか
    • Unicodeに限り、japanese はないので general か unicode か bin を選ぶ。
      • (言語によって文字の特殊性があるらしく、その言語別の定義があるが日本語はない)
  • 比較方法
    • 3種類あある
    • ci
      • 大文字小文字を比較しない
    • cs
      • 大文字小文字を比較する
    • bin
      • バイナリとして判断する
    • よく使う utf8... では ci と bin のみになる…?(何でー?)

どんな照合順序が使えるかを確認する

show collation というクエリを実行することで確認できる。

show collation;
Collation Charset Id Default Compiled Sortlen
big5_chinese_ci big5 1 Yes Yes 1
big5_bin big5 84 Yes 1
dec8_swedish_ci 8-Dec 3 Yes Yes 1
dec8_bin 8-Dec 69 Yes 1
cp850_general_ci cp850 4 Yes Yes 1
cp850_bin cp850 80 Yes 1
hp8_english_ci hp8 6 Yes Yes 1
hp8_bin hp8 72 Yes 1
koi8r_general_ci koi8r 7 Yes Yes 1
koi8r_bin koi8r 74 Yes 1
latin1_german1_ci latin1 5 Yes 1
latin1_swedish_ci latin1 8 Yes Yes 1
latin1_danish_ci latin1 15 Yes 1
...

※ like など SQL を書くときに使うような演算子を利用して絞り込みができたり、 ORDER BY 並び順を設定できる。

show collation where Collation like 'utf8%';
Collation Charset Id Default Compiled Sortlen
utf8_general_ci utf8 33 Yes Yes 1
utf8_bin utf8 83 Yes 1
utf8_unicode_ci utf8 192 Yes 8
utf8_icelandic_ci utf8 193 Yes 8

MySQL サーバーのバージョンによって使える照合順序が違うので、環境によって結果が変わることがある。(古いと utf8mb4 がないとかそういう) 5.6, 5.7 あたりならこの記事の話は問題ないはず。詳しい一覧はここで確認ができる。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 10.1.14 MySQL でサポートされる文字セットと照合順序

*_unicode_ci と *_general_ci の違い

照合順序の違いによってどんなことが起こるか、実際にクエリを発行しつつ確認する。

# バージョン確認
select version();
# 5.7.17


# アルファベットの大文字小文字、半角全角
#============================================================
SET NAMES 'utf8' COLLATE 'utf8_unicode_ci';
select 'A' = 'a', 'A' = 'A', 'A' = 'a';
# 1, 1, 1
 
SET NAMES 'utf8' COLLATE 'utf8_general_ci';
select 'A' = 'a', 'A' = 'A', 'A' = 'a';
# 1, 0, 0
 
SET NAMES 'utf8' COLLATE 'utf8_bin';
select 'A' = 'a', 'A' = 'A', 'A' = 'a';
# 0, 0, 0
 
 
# ひらがな, 半角カタカナ + 半濁音
#============================================================
SET NAMES 'utf8' COLLATE 'utf8_unicode_ci';
select 'は' = 'パ';
# 1
 
SET NAMES 'utf8' COLLATE 'utf8_general_ci';
select 'は' = 'パ';
# 0
 
SET NAMES 'utf8' COLLATE 'utf8_bin';
select 'は' = 'パ';
# 0

この結果をまとめると。

半角全角の比較 大文字小文字の比較 ひらがなカタカナの比較
unicode_ci できない できない できない
general_ci できる できない できる
bin できる できる できる

というわけで general_ci か bin を使うようにするとこのあたりの比較がちゃんと出来てよさそう。

utf8 と utf8mb4 の違い

MySQL の UTF-8 は 3 バイトの幅しかなく、絵文字などを表現するためには 4 バイトが必要。きっと歴史的な背景から utf8 と utf8mb4 が分裂している。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 10.1.10.7 utf8mb4 文字セット (4 バイトの UTF-8 Unicode エンコーディング)

先程と同じように文字コードを指定して比較していく。

SET NAMES 'utf8' COLLATE 'utf8_unicode_ci';
select 'あ' = '?', '?' = '?';
# 0, 1

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';
select 'あ' = '?', '?' = '?';
# 0, 1

SET NAMES 'utf8' COLLATE 'utf8_general_ci';
select 'あ' = '?', '?' = '?';
# 0, 0

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci';
select 'あ' = '?', '?' = '?';
# 0, 1

SET NAMES 'utf8' COLLATE 'utf8_bin';
select 'あ' = '?', '?' = '?';
# 0, 0

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_bin';
select 'あ' = '?', '?' = '?';
# 0, 0

SET NAMES 'utf8' COLLATE 'utf8_unicode_520_ci';
select 'あ' = '?', '?' = '?';
# 0, 1

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_520_ci';
select 'あ' = '?', '?' = '?';
# 0, 0

// あれ、このあたり文字化けしてるかも…? 
// あ=絵文字1, 絵文字1=絵文字2; と絵文字を比較してます。。

むむ、結構厄介。絵文字が入力される可能性があり、ちゃんと識別したい場合は utfmb4_bin を使うのがよさそう。

参考リンク

utf8_unicode_ci に対する日本の開発者の見解 - かみぽわーる
MySQL と寿司ビール問題 - かみぽわーる

最近めっちゃブログ書いているように見えるじゃろ?実は

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

某所に溜めとったネタを整形して加筆して投稿しとるんや、せやからナウで取り組んでいることとちょーっと違うことが多かったり、日によってまったく違うコンテンツになるんや、ウケるやろ、ガハハw
似非関西弁だ!!!捕らえろ!!!!

書き溜めしておいたネタは雑に書かれているものも多く、あとはそっち見ろ、みたいな。さすがにこれではブログにするのもあれなので、会社のお昼休みや帰る直前、そして通勤時間を使って、文章を整えたりコードを追記したりしている。

 
いままで都心部に住んでいたので電車で 15 分程度ゆられていたのだが、横浜の方に引っ越してからというものジャスト 1 時間くらいゆられるようになった。さすがに電車の中が暇になってしまう。そこでせっかくだからと思って、書き溜めしておいたものから順番に整形してブログに出せるようにしている。

コード書いて試したりーなどは PC からじゃないとちょっとつらいところもあるので、あとで書くように空白にしておき、周りの文章を整えキレイにしたり、言葉足らずなところを修正していっている。

そうこうして整った文書は Trello でカードに登録している。これを PC で開いてコードを追記したり画像添付を作ったりしている。出来上がったものはブログにアップし Trello のカードはアーカイブ。

Trello

平日はそんな様子で、土日もちょっと時間があくねー、みたいなタイミングで Trello を開いてちまちまブログ記事を作っている。

こんな流れで、ここ 1 ヶ月くらいはちまちまブログを書いている。夏休みが間にあってそこはまったくやらなかったから間が空いているけど 10 記事を超える投稿をした。今までの自分からしたらあり得ない量だ。

 
ブログを更新していると、デザインだったりなにやらが見にくいなあーなどとと思う箇所もあって、ちょっとずつブログの見栄えや機能も調整している。

こちらも同様に、次にこういう機能やろうかなーとか、こういう調整しようかなー、みたいなものは Trello でカード化している。行き帰りの電車でどうやるか調べたり段取りを考えたりし、あとで PC からサーバに入って実際に操作していく。こんな様子でやっている。

 
なんでこんなこと突然はじめたかというと、知り合いから「 1 日 1 つ強くなる」という話を聞いてから。これはあの有名なプロゲーマー・ウメハラの著書のタイトルにもなっている。

1日ひとつだけ、強くなる。 世界一プロ・ゲーマーの勝ち続ける64の流儀 | 梅原 大吾 |本 | 通販 | Amazon

実際にぼくが本を読んだわけではないが、これを読んだ知り合いはそれに影響を受けて、毎日知らなかった単語を 1 つだけ調べてメモしているそう。 たとえ 1 日で 1 単語調べたとしても 1 ヶ月も続ければ 30 単語 くらいは抑えられる。半年もやれば 180 単語。分野を絞ってやれば、だいぶ詳しくなる量だろう。

これのポイントはその人曰く「やりすぎないこと」だって。 1 日でやりすぎちゃうと燃え尽きて、次の日のやる気がなくなってしまうから、って本にも書いてあるらしい。

これまでは業務だったりプライベートだったりで何かと調べたりするわけだけど、だいたい頭の中に入れるて、 Twitter にシェアして、「ほー」とか言って、目の前の問題を解決したらおしまい。
それがすごいもったいないなーって思うようになって。問題を解決したのだから、後でなのか今なのかわからないけど同じ問題に当たる人を助けることができるかもしれない。せっかく勉強にしたことなのだから世の中に出していけばいい。もはや情報過多の世界なのだから、こんなちっぽけな記事が大多数の人の目に触れるなんてこともそうそうないだろう、多少汚くてもいいと思うよ。何を躊躇しているんだか。「 1 日 1 つ強くなる」でブログをかくなら「 1 日のうち多少なりともブログ記事に手をいれる 」という目標でいいじゃんか。別にその教えてくれた人に負けたくないとか張り合うとかそういう話ではなくて、「 1 日 1 つ強くなる」ためにブログを書いていこう、って思いましたとさ。

 
よく考えれば "Write Code Every Day." と同じだよね。 1 日で何かしらコードを書く、 Github へ上げて草を生やす。

毎日コードを書けば新しい発見があるだろうし、新しいことを学べるだろうし、エンジニア…あるいはプログラマー的「 1 日 1 つ強くなる」はまさにそれなんじゃないかな。

 
と。実はもう 1 つ理由があって。ある種のラインでもある 3 年が経過したわけで、そろそろ自分のことを一回考え直してみるかなーと。人生のステージも変わったわけだし。なので棚卸しじゃないけど、自分のもっていたものをいろいろと出してますよー、というかんじですね。

 
そんなこんななごみばこさんの最近のブログ事情でした。

Laravel5 で Log ファサードを使ったときに一緒に標準出力にも出した

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

artisan コマンドを作ったとき、何かと様子を出力するために Log ファサードを使ってログファイルに出している。

 

ちなみに。そのとき Monolog のハンドラを使って、レベル別にファイルを分けたりなどしている。こんな具合にするとログレベルごとにファイルが分かれる、めっちゃ便利。

// bootstrap/app.php
$app->configureMonologUsing(function ($monolog) {
    $logConfigure = config('log');
    foreach ($logConfigure['file'] as $logLevel => $logFile) {
        $path = storage_path('logs/' . $logFile . '.log');
        $handler = new \Monolog\Handler\RotatingFileHandler(
            $path,
            $logConfigure['rotate'],
            $logLevel,
            false,
            $logConfigure['permission']
        );
        $handler->setFormatter(new Monolog\Formatter\JsonFormatter());
        $monolog->pushHandler($handler);
    }
});


// config/log
return [
    'permission' => 0777,
    'rotate' => 30,
    'file' => [
        \Monolog\Logger::DEBUG     => 'debug',
        \Monolog\Logger::INFO      => 'debug',
        \Monolog\Logger::NOTICE    => 'debug',
        \Monolog\Logger::WARNING   => 'warning',
        \Monolog\Logger::ERROR     => 'error',
        \Monolog\Logger::CRITICAL  => 'error',
        \Monolog\Logger::ALERT     => 'error',
        \Monolog\Logger::EMERGENCY => 'error',
    ],
];

自分で用意する artisan コマンド、ようはバッチでは様子の出力などをファイルに出すために Log::info(); とかして、いろいろな情報をログに出ていく。この ID のデータをやるよー、とか、おわったよー、とか、色々。

しかし開発中はそれって厄介で、パッとコマンド実行してログ見て。いや、横で tail -f とかしておけば良いのだろうけど、それはそれでちょっと面倒だなあ。というか artisan コマンドって verbose なオプションをサポートしているので、それをうまく使ってログに出すと同時に標準出力にも出せないだろうか。

と思って出来たのがコレ。

namespace App\Console;

use Illuminate\Console\Command;
use Monolog\Handler\StreamHandler;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

trait LogWithStdout
{
    /**
     * execute メソッドに割り込み Monolog の設定をする
     */
    public function execute(InputInterface $input, OutputInterface $output)
    {
        // verboseなオプションが付いたらログを画面に出す
        if ($output->getVerbosity() >= $this->verbosityMap['v']) {
            $monolog = \Log::getMonolog();
            $handler = new StreamHandler("php://stdout", 'info');
            $monolog->pushHandler($handler);
        }

        // 元の処理の呼び出し
        return parent::execute($input, $output);
    }
}
namespace App\Console\Commands;

use App\Console\LogWithStdout;
use Illuminate\Console\Command;

class FetchTrafficSummary extends Command
{
    use LogWithStdout;

    public function handle()
    {
        Log::info('start!');

        ...

        Log::info('complete!');
    }
}

本当はサービスプロバイダにしてイベントにしてあげたほうがキレイなのだが、 Laravel の実装上、それは出来ないっぽい…。うぬ。。

具体的にはコマンドの発火時は取れるのだが verbose オプションがあるかどうかが取れない。 OutputInterface がイベントに入ってこないのだ。ソースも追いかけてみたがイベント引数に入ってくるのは Application インスタンスだけ。 OutputInterface を持ったものは Application から Command を呼ぶときにしか作られないらしい。

 
 
とまあ、作ってみたものの Artisan コマンドでも $this->info などして標準出力に出せるので、そちらをオーバーラップしたほうがキレイなんじゃないかなーと思った。こういうイメージ。

public function info($string, $verbosity = null)
{
    parent::info($string, $verbosity);
    \Log::info($string);
}

どちらかといえばこちらのほうが、メソッド別にログファサードのメソッドを呼べるのでパッと見でわかりやすそうだ。

TF-IDF ってのを使うと単語の重要度がわかるよって聞いたので調べた

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

TF-IDF は文章における、単語の重みづけを行うもの。
処理した文章中のうち、ある単語はどの程度重要なものか、出現度合いから重み付けを計算する。
プログラム、機械からは文章の分析っていってもよくわからないので、そのよくわからない特徴をわかるようにするための、数値化する方法の1つ。

前提として TF-IDF には複数の文章を入力する必要がある。

TF-IDF = TF * IDF

TF = 1つの文章において、ある単語の出現回数 / 文章内の単語数
→ 文章1つずつの単語の出現頻度がわかる。

IDF = log( 文章数 / ある単語が出現する文章数 ) + 1
→ 横断的に使われる単語は低い値になる。

簡単な文章を入れつつ実際の数字を計算してみる。

 
step1:文章の入力

文章
リンゴとレモンとレモン
リンゴとミカン

 
step2:単語に分割(ここでは形態素解析し、名詞だけに絞ったとする)

文章 分割した結果
リンゴとレモンとレモン リンゴ レモン レモン
リンゴとミカン リンゴ ミカン

 
step3:TF値、IDF値を計算する

文章 TF:リンゴ TF:レモン TF:ミカン
リンゴとレモンとレモン 1 / 3 = 0.33 2 / 3 = 0.67 0 / 3 = 0
リンゴとミカン 1 / 2 = 0.5 0 / 2 = 0 1 / 2 = 0.5
単語 IDF
リンゴ log(2 / 2) + 1 = 1
レモン log(2 / 1) + 1 = 1.3
ミカン log(2 / 1) + 1 = 1.3

 
step4:TF-IDFを計算する

文章 TF-IDF:リンゴ TF-IDF:レモン TF-IDF:ミカン
リンゴとレモンとレモン 0.33 * 1 = 0.33 0.67 * 1.3 = 0.87 0 * 1.3 = 0
リンゴとミカン 0.5 * 1 = 0.5 0 * 1.3 = 0 0.5 * 1.3 = 0.65

 

この結果からわかることとして。

  • 「リンゴ」は横断的に出現するので値が低くなる。
  • 「レモン」は文章1だけに多く使われるので値に特徴が強く出る。
  • 「ミカン」は文章2だけに使われているが、リンゴと同程度しか使われていないので、特徴が大きく出るわけではない。
  • この2つの文章を切り分けるには レモン の様子を見ればよいらしい。ということで、確かに単語の重要度がわかるよ、っていうのはあっているらしい。

ただ、全ての文章・全ての単語を利用するという計算ロジックの都合、文章を逐次増やして差分計算していくー、というのは出来なさそうなので、そういう用途はむりそうだなー。
例えばこの計算済み TF-IDF の結果に加えて「バナナとレモン」を追加したときに困っちゃうね、という話。この量なら良いけど、もっと文章量が増えてもっと単語量が増えると、毎回計算し直すコストがかかる。

Composer で指定したライブラリがインストールできないんだけど!!と言われたと

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

composer require コマンドでライブラリを入れようとすると「ライブラリをいれたいけど入れられないよ!」なんて怒られることがある。いや、 composer install でも起こることあるわ。

起きたこと

※既に他のライブラリを入っている状態

$ composer require --dev "phpdocumentor/phpdocumentor:^2.8"
You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug
./composer.json has been updated

Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
 
  Problem 1
    - phpdocumentor/phpdocumentor v2.8.0 requires herrera-io/phar-update 1.0.3 -> satisfiable by herrera-io/phar-update[1.0.3].
    - phpdocumentor/phpdocumentor v2.8.1 requires herrera-io/phar-update 1.0.3 -> satisfiable by herrera-io/phar-update[1.0.3].
    - phpdocumentor/phpdocumentor v2.8.2 requires herrera-io/phar-update 1.0.3 -> satisfiable by herrera-io/phar-update[1.0.3].
    - phpdocumentor/phpdocumentor v2.8.3 requires herrera-io/phar-update 1.0.3 -> satisfiable by herrera-io/phar-update[1.0.3].
    - phpdocumentor/phpdocumentor v2.8.4 requires herrera-io/phar-update 1.0.3 -> satisfiable by herrera-io/phar-update[1.0.3].
    - phpdocumentor/phpdocumentor v2.8.5 requires herrera-io/phar-update 1.0.3 -> satisfiable by herrera-io/phar-update[1.0.3].
    - Conclusion: don't install herrera-io/phar-update 1.0.3
    - Installation request for phpdocumentor/phpdocumentor ^2.8 -> satisfiable by phpdocumentor/phpdocumentor[v2.8.0, v2.8.1, v2.8.2, v2.8.3, v2.8.4, v2.8.5].
 
 
Installation failed, reverting ./composer.json to its original content.

原因

Composer は依存ライブラリの解決を行う際、それぞれのライブラリの composer.json に記載されているバージョンを解釈し、お互いに問題のない、なるべく最新のものを選択しようとします。

このとき「あるライブラリではAの1.0.* を使います。別のあるライブラリではAの2.0.*を使います」といった記述がされていると、エラーになってしまうようです。

あるいは composer.lock としてバージョンが固定されてしまうときにも同様の問題が起こります。依存を再解決しようとするときに上記のようにぶつかってしまうことがあるようです。

対応策

順番に確認していくと良さそう。

  1. そもそも composer.json で書かれているバージョンとエラーになっているバージョンとが噛み合わない
    • → 使っているライブラリ、または入れようとしているライブラリのバージョンを見直す
  2. そんなことはない、依存していたんだ、というくらいのものでエラーが出てる
    • → composer.lock を消して composer install

どちらにしても、依存ライブラリのバージョンが変わるはずなので、テストの実行や動作確認を行ったほうがよいです。

Composer は何者か。あるいは install と update の違い。そしてオートロードの仕組み。

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

たいとるそのまま。

Composer 使って Packagist から ライブラリを入れられるけど結局何者なんだ、 install と update の違いはなんだ、つーかオートロードってどうなってんねん、などなどがよくわからなかったのでドキュメントとかソースを見た。

 

Composer とは?

Introduction - Composer

このあたりを、見ると大体解決する。

Composer "Dependency Manager for PHP"
→ PHP向けの依存関係管理システム

Ruby における Bundler や Node.js における npm のように、 PHP に対しての Composer 。

Composer 自体も PHP で書かれていて、PHP 5.3.2 以降の環境ならうまく動いてくれる。
場合によっては、コンパイルオプションの指定やモジュールの追加、コンフィグの問題などが発生するが、 Composer が判別し、警告を出してくれるのでその時に対応すれば良い。

 

依存関係管理?

作っていく PHP アプリケーション(あるいは PHP ライブラリ)が、ほかのライブラリに「依存」している場合、依存しているライブラリもセットにしないと動作しない。

仮にそのライブラリがまた別のライブラリに「依存」していたら…?
あるいは、ライブラリ A と別のライブラリ B が、ある同じライブラリ C に依存しているけど、 C と C' のように違うバージョンが対象だったら…?

などなどの非常にやっかいなライブラリ依存関係を、簡単な記述で管理できるようにしたものが依存関係管理システム。

PHP なら PEAR がある。が、今この時代では Composer のほうがよく使われている…はず。

 

Composer インストール

composerのインストールは非常に簡単。

$ curl -sS https://getcomposer.org/installer | php

この場合はカレントディレクトリに comopser.phar というファイルが作られ、これがComposerの本体になる。

どのディレクトリからでも実行できるように、どのユーザでも実行できるようにするためにはシステムにインストールする必要がある。

その際には、以下のコマンドで。

$ sudo mv composer.phar /usr/local/bin/composer
$ sudo chown root:root /usr/local/bin/composer
$ sudo chmod +x /usr/local/bin/composer

 

Composer がすること

  • composer コマンドの提供
  • 依存するライブラリについて
  • 依存関係の解決(ダウンロード&展開)
  • ライブラリ検索
  • 依存関係の追加
  • 依存関係の除去
  • オートロード用ファイルの提供

 

composer コマンドの提供

Composer をインストールすると composer というコマンドが利用できる。

※リネームしていない場合 composer.phar が実行コマンド。
実行権限が無い場合は php composer.phar XXXX のように php コマンドへの入力として実行する必要あり。

composer コマンドには、おおざっぱに以下の機能がある。

  • init
    • カレントディレクトに新しく composer.json を生成する
  • require
    • 依存関係を追加する
  • remove
    • 依存関係を削除する
  • install
    • composer.json に基づいて依存関係を解決する
    • composer.lock というファイルが存在する場合は、それに従う。
  • update
    • composer.json に基づいて依存関係を再度解決し composer.lock ファイルを更新する
  • dump-autoload
    • オートロード用ファイルを生成する
  • search
    • ライブラリを Packagist から探す
  • self-update
    • composer コマンド自体を更新する

他にも細かいところがあるので、気になったらドキュメントへ。
Command-line interface / Commands - Composer

 

composer install と composer update の違い

install

  • 7文字
  • composer.lock があればそれに従ってライブラリを導入する
  • composer.json に従ってライブラリを導入する

update

  • 6文字
  • composer.lock があっても composer.json に従ってライブラリを導入する

composer.lock は実際にインストールされているライブラリ、バージョンが記載されている。
composer.json には、このアプリケーション(ライブラリ)が一体どのライブラリ、どのバージョンに依存しているかを記載する。そのときライブラリのバージョンアップを形容できる書き方がある。

 

composer.json におけるバージョン指定の方法

詳しくはドキュメントにまとまってる。
Versions and constraints - Composer

ざっくりまとめると。

  • 固定値
    • 1.0.2
  • 範囲指定
    • > >= < <= != ||
    • 記号は書いたそのままの意味、|| は OR 。スペースで AND 。
    • >=1.0
      • 1.0 以上
    • >=1.0 <2.0
      • 1.0 以上 かつ 2.0 未満
    • >=1.0 <1.1 || >=1.2
      • (1.0 以上 かつ 1.1 未満)または 1.2 以上
  • ハイフンの範囲指定
    • 1.0 - 2.0
      • >=1.0.0 <2.1 と等価
      • 1.0 以上 かつ 2.1 未満
  • ワイルドカード
    • 1.0.*
      • >= 1.0 <1.1
      • 1.0 以上 かつ 1.1 未満
  • チルダ
    • 指定した一番小さいバージョンの変化を形容する
    • ~1.2
      • >= 1.2 <2.0
    • ~1.2.3
      • >= 1.2.3 <1.3
  • ハット
    • セマンティックバージョニングに従う = メジャーバージョンを上げない
    • ただし 1.0 未満はマイナーバージョンに合わせる
    • ^1.2.3
      • >=1.2.3 <2.0.0
    • ^0.3
      • >=0.3.0 <0.4.0

チルダ + マイナーバージョン指定にしておけば、大きな変更に殺されることはなさそう。

 

Composer の提供するオートローダーとは

composer dump-autoload を実行すると、 composer.json の内容にもとづいてオートロード用のファイルが作られる。ファイルは複数あり、以下の場所で確認できる。

  • vendor/autoload.php
  • vendor/composer/*

これらのファイルは次のような仕組みで成り立っている。ソースにコメントをつけて流れを見ていく。

<?php
// vendor/composer/autoload_real.php を読み込み、そちらに処理が移る。

require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitaf2b1a382e2c643bceff8e5e7a5cce1d::getLoader();

vendor/composer/autoload_real.php

<?php
// 余談だけどこのハッシュ値っぽいものは指定できる。
// composoer config に autoloader-suffix があればそれで、なければ md5(uniqid('', true)) が使われる。
//
// composer.json に書くなら config > autoloader-suffix になるようにする。
// composer config autoloader-suffix HogeeeHugaaa でも可。
class ComposerAutoloaderInitaf2b1a382e2c643bceff8e5e7a5cce1d
{
    // ClassLoader インスタンス
    private static $loader;

    // spl_autoload_register によって利用される ClassLoader を読み込む
    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }

        // ClassLoader の読み込み
        spl_autoload_register(array('ComposerAutoloaderInitaf2b1a382e2c643bceff8e5e7a5cce1d', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInitaf2b1a382e2c643bceff8e5e7a5cce1d', 'loadClassLoader'));

        // PHP で、かつ Zend Guard Loader の拡張が有効なら autoload_static.php を利用する
        // 余談)Zned Guard Loader は Zned Optimizer によって作られたエンコードされた PHP ファイルを実行するためのもの
        // http://www.zend.com/en/products/guard/zend-optimizer-zend-loader
        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
        if ($useStaticLoader) {
            require_once __DIR__ . '/autoload_static.php';

            call_user_func(\Composer\Autoload\ComposerStaticInitaf2b1a382e2c643bceff8e5e7a5cce1d::getInitializer($loader));
        } else {
            // 条件に該当しなかった場合は、各ファイルを個別に読み込む

            // 名前空間の定義がされたものを読み込み
            $map = require __DIR__ . '/autoload_namespaces.php';
            foreach ($map as $namespace => $path) {
                $loader->set($namespace, $path);
            }

            // PSR-4 形式で定義されたものを読み込み
            $map = require __DIR__ . '/autoload_psr4.php';
            foreach ($map as $namespace => $path) {
                $loader->setPsr4($namespace, $path);
            }

            // 直にクラスが指定されているものを読み込み
            $classMap = require __DIR__ . '/autoload_classmap.php';
            if ($classMap) {
                $loader->addClassMap($classMap);
            }
        }

        // 読み込んだファイル群を spl_autoload_register に登録する
        $loader->register(true);

        return $loader;
    }
}

vendor/composer/ClassLoader.php (抜粋)

// spl_autoload_register を使って登録する
public function register($prepend = false)
{
    spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}


// spl_autoload_register によって呼ばれるメソッド
// ファイルが見つかれば include する
public function loadClass($class)
{
    if ($file = $this->findFile($class)) {
        includeFile($file);

        return true;
    }
}


// オードロード要求のクラスが含まれるファイルを探す
public function findFile($class)
{
    // classmap ファイルから読み込んだリストにいれば、それを返す
    if (isset($this->classMap[$class])) {
        return $this->classMap[$class];
    }

    // 「ファイルが見つからなかった」がキャッシュされていれば当然見つからない
    // あるいは dump-autoload のときに classmap-authoritative が指定されていれば classmap だけにする
    if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
        return false;
    }

    // APCu による最適化が有効になっていれば APCu から読み込めるか試みる
    if (null !== $this->apcuPrefix) {
        $file = apcu_fetch($this->apcuPrefix.$class, $hit);
        if ($hit) {
            return $file;
        }
    }

    // クラス名 .php のファイルを探す
    $file = $this->findFileWithExtension($class, '.php');

    // HHVM で動いているなら .hh ファイルを探す
    if (false === $file && defined('HHVM_VERSION')) {
        $file = $this->findFileWithExtension($class, '.hh');
    }

    // APCu 最適化が有効なら APCu にキャッシュ登録する
    if (null !== $this->apcuPrefix) {
        apcu_add($this->apcuPrefix.$class, $file);
    }

    // 「ファイルが見つからなかった」のキャッシュ
    if (false === $file) {
        // Remember that this class does not exist.
        $this->missingClasses[$class] = true;
    }

    return $file;
}


// クラス名 + 拡張子 のファイルを探すメソッド
private function findFileWithExtension($class, $ext)
{
    // PSR-4 形式のファイルを探す
    $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

    $first = $class[0];
    if (isset($this->prefixLengthsPsr4[$first])) {
        $subPath = $class;
        while (false !== $lastPos = strrpos($subPath, '\\')) {
            $subPath = substr($subPath, 0, $lastPos);
            $search = $subPath.'\\';
            if (isset($this->prefixDirsPsr4[$search])) {
                foreach ($this->prefixDirsPsr4[$search] as $dir) {
                    $length = $this->prefixLengthsPsr4[$first][$search];
                    if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
                        return $file;
                    }
                }
            }
        }
    }

    // PSR-4 のフォールバック
    // ※ 名前空間 => ディレクトリ名 という指定をしたもの
    foreach ($this->fallbackDirsPsr4 as $dir) {
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
            return $file;
        }
    }

    // PSR-0 形式のファイルを探す
    if (false !== $pos = strrpos($class, '\\')) {
        // namespaced class name
        $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
            . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
    } else {
        // PSR-0 は PEAR 形式のファイル名も形容するので、それを探す
        $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
    }

    if (isset($this->prefixesPsr0[$first])) {
        foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
            if (0 === strpos($class, $prefix)) {
                foreach ($dirs as $dir) {
                    if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                        return $file;
                    }
                }
            }
        }
    }

    // PSR-0 のフォールバック
    // ※ プリフィックス => ディレクトリ名 という指定をしたもの
    foreach ($this->fallbackDirsPsr0 as $dir) {
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
            return $file;
        }
    }

    // PSR-0 のインクルードパスの解決
    if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
        return $file;
    }

    // 見つからねーもんは見つからない。
    return false;
}


// 指定したファイルを include する
// ※これだけグローバル空間にいる
// ※includeしたものから $this や self を利用できないように、のため
function includeFile($file)
{
    include $file;
}

で、autoload_real から 読み込まれるファイルがいくつかあるけどなんやねん、という話は composer.json に書く。書き方はドキュメントに詳しくある。

The composer.json Schema - Composer

ざっくりとまとめると。

  • PSR-4
    • 名前空間でディレクトリを切る
    • プリフィックス : ディレクトリ名 として名前空間を設定できる
  • PSR-0
    • 名前空間でディレクトリを切る
    • プリフィックス : ディレクトリ名 として名前空間を設定できる
    • PSR-4 との違いは アンダースコアでも切ることができる、名前空間なしファイルが存在すること
  • classmap
    • 名前空間など PSR-0 または PSR-4 に従っていないもの
    • クラスのあるディレクトリやファイルを直に指定する
  • file
    • 細かいこととかいいから、俺はこのファイルを読み込みたいんだ

こんな感じ。 PSR-0 と PSR-4 はざっくりだけど、詳しく書くともっと長くなるのでまた。

 

オートローダーの最適化

このあたりのざっくりまとめ。

Autoloader Optimization - Composer

特にオプションなしに composer install または composer update 。 あるいは composer dump-autoload を実行すると、上記のまま処理が動く。

多数のライブラリを読み込むような巨大なフレームワーク、アプリケーションなどは、特に PSR-0 や PSR-4 のディレクトリ構成を解析して読み込む件で時間がかかってしまう。そこで、事前にオートローダーを最適化することができる。 -o オプションをつけるとよい。

$ composer dump-autoload -o

これで PSR-0 も PSR-4 も全てのオードロードの記述が classmap に変換される。 classmap になることで、クラス名からディレクトリを探しにいくことがなくなりその分高速化が見込める。

 
さらに本番環境など、ソースがもりもり変わることがなければ -a というオプションも付けるとよい。上のソース解説で出てきた classmap-authoritative のくだり。

$ composer dump-autoload -o -a

このオプションがあれば classmap に見つからないときに処理を諦める。そもそも composer dump-autoload なしに本番のソースが変わることはないだろうので、つけたほうが良い。が、可能性として、クラスが見つかりません!になってしまうことがある。

 
何かしらの都合で -a オプションはだめだー!となるなら APCu を有効にする戦略も取れる。 -a に比べて遅くはなるが、クラスが見つかりません!問題に対応しやすくなる。とはいえ APCu はメモリに乗っかってくるので、メモリに余裕があるような環境じゃないと使えない。

$ composer dump-autoload -o -apcu

上のソースを見てわかるとおり classmap-authoritative と APCu は同居しない、どちらかしか使えない。


複雑に絡み合ったひもがちょっとずつ解けていくように Composer の動きに詳しくなっていく気持ちがする。

が、ちょっとつかれたのでここでおちまい。

CSS設計の教科書を読んだ途中まで

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

こんにちは、ごみばこです。

最近になってようやく、ちゃんとしっかり CSS の教科書を読みました(設計の大事さと紹介まで)。そのメモというか、まとめです。CSS 設計マジ大事やなー。

Web制作者のためのCSS設計の教科書 モダンWeb開発に欠かせない「修正しやすいCSS」の設計手法 | 谷 拓樹 |本 | 通販 | Amazon

余談ですけど、ここのところブログをちょこちょこ書いているのですが、トップページが遊び要素メインで中身が無いと見なされてか Adwords も Amazon アソシエイトも審査落ちしたので、ごみばこいんからのリンクはノーアフィリエイトです。かなしみ、ビールの一杯くらい稼いだっていいじゃない

CSS設計がなぜ重要か

フロントエンドも作って終わりではない。
web サイトのコンテンツが増えたり、キャンペーンをしたりなど、 web サイトの内容が変わっていくので、それに合わせて各コードのメンテナンスをしていかないといけない。それはもしかしたら誰かに引き継ぐかもしれないし、自分でやるかもしれない。リッチな表現が求められる今の web サイトでは HTML も Javascript も CSS も複雑なものになっていく。

中でも CSS はシンプルに書ける一方でたくさんの記述が必要がゆえ、複雑なことをしようとすると、スパゲッティコードになりやすい。だから設計というルールを設けていく。
プログラミングと同様に CSS も設計をすることで、予測しやすい、再利用しやすい、保守しやすい、拡張しやすい、ものを記述できるようになる。

設計をどうやっていくか

見た目の変更や入れ替えがあるなかで、使いまわすようなものも多くある。その使いまわしのために、要素を部品=モジュール、コンポーネントと考えていく設計が有効になる。
そのコンポーネントとして考えるために、いくつかの実際に活用されている設計方針を紹介していく。

OOCSS

Object-oriented CSS

構造と見た目の分離、コンテナとコンテンツの分離を挙げている。
コンポーネントとしての構造、ブラウザ上での見た目を分離して考えること。ある要素がどこにいるかのコンテナ、ある要素のコンテンツを分離して考えること。

後者は難しいが「場所に依存しないセレクタを書くこと。どこそこの中にある○○、とし定義されるようなものを避けること」と考えるとわかりやすい。

SMACSS

Home - Scalable and Modular Architecture for CSS

OOCSS で言われている分離を実践するためのカテゴリが決められている。

Base :見た目のリセット、標準化
Layout:全体的なレイアウト設定(2カラムなの、3カラムなの、ヘッダー?)
Module:コンポーネント自体
State :コンポーネントの状態
Theme :コンポーネントの見た目違い

もう一つのSMACSSのポイントとして「HTMLとCSSの分離」が挙げられている。

要するに。
SMACSS = OOCSS + OOCSSを使うためのカテゴリ + HTMLとCSSの分離

BEM

BEM — Block Element Modifier

CSSにおける命名規則の提案。OOCSS、SMACSSを考えてのコンポーネントを記述できるよう規則が決められている。

Block
Elementの集合体、ひとつのコンポーネント
命名は自由

Element
コンポーネントに含まれる要素の1つ1つ
命名は Block_Element という形式

Modifier
BlockとElementについて、場合分けが起こるときに使う
命名は Block__Modifier または Block_Element__Modifier という形式

たしかにBEMでは、アンダースコアを付けるように、と言っているがそれはさほど重要ではない。キャメルでもハイフンでもいい。コンポーネントの体に合わせた命名規則を設けることで、シンプルにたくさん書けるCSSへ規律をもたらし、事故を未然に防いだり可読性を上げることが大事。

MCSS

MCSS

SMACSS にレイヤーというルールを足すようなもの。複数のレイヤーを構成することによってCSSを設計する。レイヤーとして縛りを設けることで、CSSプロパティの汚染を防いでいく。

Foundation
プロジェクトの土台、resetcssやnormalize.cssがそれ
最初に読み込まれる

Base
コンポーネント定義
他のレイヤーから拡張変更ができる必要がある
Baseレイヤーコンポーネントは上書きできる

Project
具体的なページを構成する要素、Baseレイヤーコンポーネントの集まり。現実的にはこのレイヤーが厚くなりがちだけど、上書きによって階層が深くならないようにする
Baseレイヤーコンポーネント、Projectレイヤーコンポーネントを上書きできる

Cosmetic
下層レイヤーコンポーネントに含まれないちょっとしたスタイル。ヘルパー、ユーティリティのようなもの、グローバルなModifierなど。
基本的に上書きは出来ない

Context
例外的なレイヤー。すべてを上書きできる。
特定のブラウザ、デバイス、状況によって切り替えるようなもの。PC / スマホ、ログインしてる / してない。
セクションを持たず、各コンポーネントについて書かれるセクションの中で記述される。

レイヤーの分け方に合わせて、CSSを記述するときの基本原則がいくつかある。

  • コンポーネントを1つのセクションにまとめる
  • Modifierを個別クラスにし、単体で利用しない
  • コンポーネントのカスケーディング(上書き)が出来る
  • あるコンポーネントに含まれる別のコンポーネントに適用するスタイル。ただし上層のコンポーネントを上書きしてはいけない。同じ層か、下層のみ上書き可能
  • クラス名を略語にする

FLOCSS

この本の筆者が考えている、全部まぜのいいとこ取り。

ソースファイルのセクションわけ、レイヤー制約、命名制約が盛り込まれている。詳細は Github で!
hiloki/flocss: CSS organization methodology.

ルールこそ多いように見えるが、ルールに乗れると非常に楽(体験談)

ここまで読んで

個人的なちょっとしたやつで Bootstrap を入れた上で、新たに書き足す部分を FLOCSS を意識して作ったところ、非常にわかりやすく、事故もなく、安心安全にできてちょっと感動してました。
…、とはいえ FLOCSS でみんなやろうぜ!というわけではなく。本にも書かれているのですが、色々な考え方があるけど俺達はどうやってやるよ、というのを改めてちゃんと考えないとね、って思いましたとさ。

いじょ。

算術平均を逐次計算したい。

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

数式をこねくりまわす知識や技術はないので自分で求められないのですが、平均値って逐次計算できるのかな?を調べたメモ。

x = [...] // 元の数列
z = ...   // 追加したい数

// 平均値
Average(x) = Sum(x) / Count(x)

// 数を追加した平均値
Average(x + z) = Average(x) + ((z - Average(x)) / Count(x))

調べていたらこういう式も。平均に個数をかけて数を足し、そこから新しい個数で割れば、新しい平均だよね、というもの。そりゃそうだわ。

Average(x + z) = (Average(x) * Count(x) + z) / Count(x + z)

どちらにしても個数と平均値だけ持っておけば逐次計算できますね。後者は掛け算するので、個数や平均値が大きすぎると扱えなさそうなので、前者のほうがよさそう。

試しに実装してみるとこういう形。

class MeanStream
{
    private $mean = 0;
    private $dataCount = 0;

    public function __construct() {
    }

    public function add($num) {
        $this->dataCount++;
        $this->mean = $this->mean + (($num - $this->mean) / $this->dataCount);
        return $this;
    }

    public function get() {
        return $this->mean;
    }
}

echo new MeanStream()
    ->add(1)
    ->add(7)
    ->add(8)
    ->add(6)
    ->add(2)
    ->add(5)
    ->add(1)
    ->add(8)
    ->add(9)
    ->add(4)
    ->get();

echo (1+7+8+6+2+5+1+8+9+4) / 10;
// 5.1

あってますねー

 
ちなみに、当然のことながら上記の例だと浮動小数点数を使うので、使う値によっては表現の都合で微妙に数値が変わるのでその点だけ注意ですね。

$values = [];
for($i = 0; $i < 10; $i++) {
    $values[] = mt_rand();
}

$mean = new MeanStream();
foreach ($values as $value) {
    $mean->add($value);
}

echo sprintf('%.30f', array_sum($values) / count($values));
// 995988838.500000000000000000000000000000

echo sprintf('%.30f', $mean->get());
// 995988838.499999880790710449218750000000

PHP じゃない言語で実装するとして、もし Decimal のような、よろしく精度をもってくれる機能があればそれを使ってあげるほうがよさそうです。PHP は言語として標準に Decimal がないのでどうするんだろう、 bcmath とか gmp というのがありそうだけど extension に左右されるなー。まあ composer.json で extension について依存あるよーと記載すればいいのか。
PHP: 数学 - Manual

Packagist みると幾つかありそうなので、このあたりを使ったほうがいいのかなー。

 
と、話がそれてきたあたりでおしまい。ライブラリの話はまた今度。

Atom と Hydrogen で Jupyter が Atom で動くよ!(インストール、準備、使い方まで)

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

一時期 Atom を使ってたけど、もっさり感がどうにもならないのと、簡易的なメモ(新規ファイル作って保存せずメモしておきたい、エディタ再起動しても残っていてほしい)が残せなかったっぽいのもあって、しばらくずっと PhpStorm か SublimeText を使っています。

最近になって、普段使いの PC を新しいものに変更してスペックも上がったし Atom もいろいろバージョンアップしているようだしどうだろうなあ、と思って Atom のプラグインを色々見てたら Jupyter を触れるとかなんとか書いてあるではありませんか!
謎技術だなあと思いつつもエディタ上で Jupyter のように振る舞ってくれるならそれはとっても素敵だなーと思ったので、試してみたの巻。

 

Atom + Jupyter をやるには?

Hydrogen というパッケージを使うと Atom 上で Jupyter かのように一行ずつ実行したり、結果をグラフィカルに表示したりが出来ます。

内部的にはインストールされている Python で Jupyter を起動。 Jupyter が用意しているメッセージプロトコルを利用して nodejs と通信し、その結果を Atom で見えるように整形している、というものです。

参照)nteract/hydrogen: Run code inline in Atom using Jupyter kernels
※ How it works のくだり。

この Hydrogen を Atom にインストールしてやります。
それでは、順番に環境を準備していきましょう。

Atom

とくに言うことはないです。公式サイトからどうぞ。

Atom

Python の準備

こちらも同様に公式サイトからどうぞ。バージョンは 3 系で良いです。執筆時時点なら 3.6 あたりで良いと思います。
もし Windows を使っているなら Windows 用インストーラを選ぶとよいでしょう。

Welcome to Python.org

Jupyter の準備

少々雑ですが、いろいろ pip install したら良いです。

pip install -U pip

pip install ipython
pip install jupyter
pip install matplotlib

// 試しに利用したい //
pip install numpy
pip install scipy
pip install pandas
pip install scikit-learn

※ Windowsの場合
主に numpy と scipy と scikit-learn あたり(多分)でコンパイルエラーになってしまうので、別途配布しているサイトから whl ファイルをダウンロードして、それを使って登録する必要があります。

Python Extension Packages for Windows - Christoph Gohlke

pip install <ダウンロードした whl ファイル>

Hydrogen を入れる

Atom 内のパッケージ管理からインストールできます。Hydrogen で検索すれば一発でしょう。


(既にインストール済みなのでアンインストールボタンですが、通常はインストールボタンになります)

Atom + Hydrogen を試してみる

これ以上の準備は不要です。 Atom 上で Python を書いて、おもむろに Ctrl + Alt + Enter を押すと、勝手に Jupyter が裏で起動し、ソースを実行してくれます(スゴイ)
一括実行やブロック実行もできるので Jupyter がそのまんま Atom 上で動いているようにも見えます。

試しに numpy でランダムなデータを作成し KMeans で 5 つに色分けしてみた結果です。


 
キャプチャの通り Windows ですが scikit-learn も動きますし matplotlib の結果もちゃんと Atom 内で表示してくれます、スゴイ!!

え?Tensorflow?Chainer?
動くんじゃないですか? Atom で Python が動いている!!!ように見えるだけで、裏側は結局 Python + Jupyter でしかないので、それが動く環境なら動作するはずです。

Hydrogen の使い方

Jupyter ではそれぞれの Cell が明確に入力欄が別れていたり、なんとなく Ctrl + Enter を押したり、実行できそうなボタンを押すなどして動かしていましたが、 Hydrogen ではただの Atom エディタになるので明確に Cell の境界が見えることはありませんしどうなっているのでしょう。

公式ドキュメントにかかれていることとほぼおなじなので、そちらを見ている場合はこっちは見なくても良いです。
Getting Started · Hydrogen

コードの実行

デフォルトでは以下のようなキーバインドがされています。

キー コマンド 処理内容
ctrl-enter hydrogen:run 選択されているコードのみを実行する
ctrl-alt-enter hydrogen:run-cell 現在のセルを実行する
alt-shift-enter hydrogen:run-cell-and-move-down 今のセルを実行し、次のセルに移る
ctrl-alt-shift-enter hydrogen:run-all 全てのセルを実行する

セルを分けるには Jupyter と同じ記述の # In[ ]: という形を使うのが良さそうです。

# In[]:
x = 10
y = 10
(x, y)  # 10, 10

# In[]:
z = x * y
z  # 100

実際に試してる様子はこんな感じ。左からコード実行、セル実行、全実行。

カーネルの再起動

これはコマンドパレットから。なんか挙動がおかしいぞ?って時によく使いますよね。使わない…?

gif 見ての通り、再起動しても出力結果は消えないので、そこも消したいんだけど!!!という場合には Clear Result を別途実行する必要があります。

出力エリアのトグル

デフォルトでは、コードのすぐ下に出力されます。これはこれで便利ではあると思うのですが、コード量が増えてくるとうっとうしく感じることもあるかもしれません。 Hydrogen には出力エリアが準備されていて、それを開くことで、そちらに全て出るようになります。
しかもページ分けされて便利!

変数ウォッチ機能

これ Jupyter にありましたっけ…?あったら知らない私がごめんなさいなんですけど。。ともあれ、この機能めっちゃすごくないですか、変数を変えていくとリアルタイムで出力結果を確認できるんですよ…、どうなってんねん…。

クラス、関数のインスペクション

ライブラリだったり、自分で実装したクラスや関数について、説明を見ることが出来ます。公式ドキュメント片手にコードを書くことが多い私的には便利に使えそうです。

コード補完

ライブラリの中、変数の中に結局こういうメソッドってあったっけ?をドキュメントに見に行ったり私には非常に嬉しい機能です。
注意点としては、すぐ補完が効くわけではなく、その変数やクラスがカーネル側に認識されていないといけません。つまり、一度そのコードを実行して、認識させる必要があります。以下の gif では Ctrl+Enter を押して pred をカーネル側に認識させています。

おわりに

というわけで Atom + Hydrogen で Jupyter Notebook が使えるよ、って話でした。

Jupyter それ自体は非常に便利ですが、普段使うエディタを勝手が異なったりすると、それはそれで使いにくいですよね。。ショートカットキーだとか、テーマだとか…。。
Atom 自体がそこそこ重たいので、スペック高めのPCでないと難しいかもしれませんが、 Python 用のエディタとして Atom がかなり有力な選択肢になるんじゃないでしょうか?

ぜひお試しあれ!

1 2