ひびのログ

日々ではないけどログを出力していくブログ

「コメント」と「コメントアウト」と「コメント化」の違い

プログラミングをしているときにわからないことがありググったときに、 「コメント」を使用すべき箇所で「コメントアウト」という単語が使用されていることがままあります。

プログラミングにおいて、「コメント」と「コメントアウト」、そして「コメント化」はそれぞれ違う意味を持ちます。

「コメント」とは

「コメント」とは、 「ソースコード中にメモなどを記述するために、特別な構文等を使用して記述した、 プログラムとして解釈されない文字列、またその内容」 です。

英語の「comment」の「注釈・解説」という意味そのままです。

コメントを記述するための「コメント構文」を使用して書かれている文字列全般を指します。

コメント構文には、

  • C/C++やJava等では「//」や「/* ~ */」など
  • RubyやPython等では「#」など

が用いられます。
こちらは、言語によって定められています。

ここで言いたいことは、「コメント」は単なる「文字列」ということです。

「コメントアウト」とは

こちらは、 「コメント構文を使用して、ソースコードをコメントに変え、 一時的にプログラムを無効化すること」 を言います。

ソースコードをコメントにするとプログラムとして解釈されないため、 ソースコードを削除したように動かすことができます。

デバッグを行うときによく利用されています。

「この行を削除したらどうなるんだろう? でも削除してしまうと戻せなくなってしまう……」
そんなあなたにコメントアウト!
簡単に削除した動きが実現できます!

英語の「out」がくっついたことにより、「コメントによって取り除く」といった意味になります (プログラムの動きの外に出すイメージです)。

言いたいことは、「コメントアウト」は「自分が行う動作」です。

「コメント化」とは

「コメント化」と言うこともあります。 これは、「文字列がコメントになる」という意味です。

よく「コメント化する」というように使われます。

これは「状態の変化」です。

まとめ

  • 「コメント」は「文字列」
  • 「コメントアウト」は「自分が行う動作」
  • 「コメント化」は「状態の変化」

ですので、これらはすべて違うものです。

間違えないように注意しましょう!

例文

  • コメントにはプログラムの意図を書きましょう。
  • ここがコメントになっていないのでエラーが発生している。
  • コメントアウトしてデバッグしてみよう。
  • コメントアウトしているところを元に戻そう。
  • この行はコメント化してください。

EditorConfigをVimに導入したら結構便利だった

便利だったので布教してみます。

導入のきっかけ

Vue.jsを勉強してみようと思って、vue-cliでプロジェクトを作ってみたところ、2スペースインデントに苦しめられたので。

.vimrcいじるほどではないなーと感じたんですが、作ったプロジェクトの中に.editorconfigがあるのを発見して、「ならば導入してみよう」となりました。

EditorConfigってそもそも何?

エディタの設定をいじることなく、独自の設定ファイルを記述することで、プロジェクトごとのフォーマットの設定ができるという画期的な仕様。

大抵はエディタに内蔵されていたり、プラグインとして提供されていたりする。

詳しくは公式へGo!

editorconfig.org

Vimへの導入方法

dein.vimを使っています。

call dein#add('editorconfig/editorconfig-vim')

以上です! 簡単!

その他のエディタ

内蔵されていたりプラグインがあったりします。詳しくは公式へ。 手書きのロゴがとってもかわいい!

…………Eclipse? 知らんな。

使い方

.editorconfigファイルがあれば、勝手に読み込まれてその設定が使用されます。

自分で定義しておくことも可能です。というかしたほうがいいです。

使ってみた感想

タイトルにもあるけど、結構便利。

vue-cliなど、テンプレートをダウンロードしてきてそのまま使うならほぼ必須だと思います。 自分はタブインデント派なので、大抵のJSプロジェクトで設定が必須です。

マイ.editorconfig

これをホームディレクトリに配置しています。 上のディレクトリを見に行かないため、root = trueを設定したファイルを置いています。

大抵の.editorconfigには設定してあるとは思いますけどね。

github.com

懸念事項

深いディレクトリでVimを起動すると、親のディレクトリに.editorconfigを探しに行くので、Vimの起動が遅くなる?(未検証)

Pythonの好きになれないところ5選

最近機械学習でPythonが日本でもにわかに話題となっていますが、ワタシパイソンスキジャナイ......
その理由をつらつらと書いていきたいと思います。

注意

あくまでPythonという言語に対する個人的な感想であり、Pythonに関わる方々を批判しているわけではありません。 「そう思う人もいるんだー」程度に見ていただけると幸いです。

1. 2系と3系

よく言われることだと思いますが、移行するなら早く移行してほしいです。 すぐにでも2系をdeprecatedにして、どんどん3系に移行してください。 2系でしか使えないライブラリとかがあると萎えます。 2系のEOLが2020年らしいので、そこまでの辛抱ですが……

とりあえず、3系で学習した後にわからないことがありググったときに2系の情報が出るのは、とても紛らわしいのでやめていただきたいです。

2. メソッドと関数の使い分け

例えばJavaなら、ソートしたい場合は、list.sort((a, b) -> a - b)とします。Listオブジェクトのメソッドを呼び出します。
あるいはリストの長さを取得しようと思ったら、list.size()とします。こちらもListオブジェクトのメソッドを呼び出します。

一方Pythonの場合、
リストをソートしたい場合は、list.sort()とします。リストのメソッドを呼び出しています。
一方リストの長さを取得する場合は、len(list)とします。関数にリストを渡しています。

Javaではリストに対する操作はListのメソッドを呼べばよかったのに対し、 Pythonではメソッドか関数か判断してコーディングしなければいけません。 本題とはちょっと違いますが、自分はJavaを書くときに、オブジェクト名.まで打つと表示されるメソッド一覧から選ぶことがあるため、 それが通用しないとなると途端にコーディング速度が落ちます。

要するに、いちいちどっちか自分で判定したくないんです。


実はPythonにはsorted(list)list.sort()の2種類のソート方法があります。 前者は関数、後者はメソッド呼び出しですね。 「ただでさえわかりづらいのに2種類あるのかよ!」と言いたくなる気持ちはありますが、これには合理的な理由があります。

関数呼び出しの方は「副作用なし(listが変更されない)」、メソッド呼び出しの方は「副作用あり(listが変更される)」です。 つまり、使用するときにはnew_list = sorted(list)list.sort()のように、使い分けが必要です。

これは先程のソートと長さの取得でも当てはまります。 list.sort()は副作用ありなのでメソッド、len(list)は副作用なしなので関数となっています。 わかりやすい! ここは思わず「なるほど」となりました。

ただし。

len()は副作用なしの関数と言いましたが、内部では__len___メソッドを呼び出しているようです。

(参考:Pythonを書くための最低限の文法メモ - Qiita

……関数とメソッド分けてる意味ないじゃん?


とPythonばっかり責めていますが、JavaもJavaでひどいです。 list.sort()と同様にソートするCollections.sort()があります。 これ、両方とも副作用ありで、listオブジェクト自体が変更されます。 CollectionsのJavaDocにあるように、

この実装は、指定されたリストとnullコンパレータを使用し、List.sort(Comparator)メソッドに従います。

とあるため、なんで存在するのかわかりません。

ちなみにScalaだとリスト(Seq)は基本immutableなので、副作用がないメソッドしかありません。 mutableのリストのソートについては知りません。使わないので。

3. リスト内包表記

これもわりとよく言われるかなと思います。

[ i * 10 for i in range(100) if i % 3 == 0]

こんな感じのやつ。

個人的には

for i in range(100):
    if i % 3 == 0:
        yield i * 10

こっちのほうが読みやすくて好きです。 でもこう書くと、「Pythonらしくないー」とか「実行速度がー」とか言われるのです。 これも慣れですかね?

4. インデント構文

自分は C→Java→JavaScript→Scala→TypeScript みたいな感じで言語を学んできたので、ブラケットで囲むのが普通なんですよね。 なのでインデント構文というのが好きじゃないです。 ちなみにRubyとかのif-endif構文もあまり好きじゃないですが、シェルスクリプトを最近触っているので、なんとなく慣れてきました。 インデント構文も慣れればいいんですかね。

ただ、これにはわりと明確な理由があります。 前述したリスト内包表記ですが……

[ i * 10 for i in range(100) if i % 3 == 0 ]


一体どこで改行できるんだ……!?


今この長さだから大丈夫だけど長くなったら見づらいし、 インデント構文だから変に改行したらプログラム実行できなくなりそう……

初見だとこうなりませんか?

ちなみに試したところ、forとかifの前で改行して、インデントあり・なし両方問題なく動きました。 ついでに最後の]だけ改行しても動きました。

インデント構文が嫌だという意見に対して大体言われるのが、「誰が書いても同じようなプログラムになるから見やすい」というものがあります。

……いやいや、それはコミット前にLintとかフォーマットとかやればいいじゃないかと。

構文レベルで縛る必要はないんじゃないかと思います。 人それぞれ書きやすいフォーマットは違うわけですし、特に個人開発では好きにさせてください。

5. if __name__ = "__main__"

importされたときに実行されないようにするための魔法の呪文ですね。

これは発想が逆かなーと考えています。

すべてimportできるようにするのではなく、importされる側が許可したものだけがimportできるようにすべきではないでしょうか? 何でもかんでもimportできる現状では、モジュールとして使われると思っていなかったファイルまでモジュールとして使われるかもしれません。 「そんなふうに使われるなんて思ってなかった!」というのは、プログラマにとっては結構馴染み深い問題だと思っています。

実際JSはmodule.exportsしないとimportできないので理にかなっているかなと。

選外

  • from xxxx import yyyyの順番が英語的に変。
  • JSやRubyでも似たようにかけますが、Pythonのスライスはプラス一癖。Scalaの記号メソッド乱立に似た何かを感じます。
  • モジュールをディレクトリ分けするときに__init__.pyを置かないといけないのが不便。
  • 機械学習でほぼ強制的に触らされるのが嫌です。

おわりに

選外の最後に書いたように、最近機械学習で触る機会が多かったのでPythonが槍玉に挙げられましたが、 多かれ少なかれ、これまで学習した言語で不満がないものはありません。

また、Pythonにもナイスだと思うところはありますし、 上で紹介した内容でも時と場合によってはナイスだと思うこともあります。 2系と3系は時間が解決しますし、初学者の方にはインデント構文が力を発揮します。

触ってみないとわからないことが多々あったので、実際に書いてみるということは大事だなと思いました。

「上で触れた内容以外にもあるよ!」とか、「それはこういう理由があるよ!」とかあれば教えていただけると幸いです。

npmパッケージ作成奮闘録

先日作成したnpmパッケージについて、所感等まとめておこうと思います。

npmパッケージ作りました

詳しくはこちらのQiitaのページを参照してください。

qiita.com

内容としては、「insertAdjacentHTML」を使いやすくしよう!ということで作成しました。 一番の狙いは、文字列ではなく関数として定義しておくことにより、エディタによる補完の恩恵を受けられるようにすることでした。 あとは文字列を覚えるのが面倒だったので、リファレンス的にも使えるかな、と思っていました。 内容的には簡単なラッパーなので、上記の理由があってもライブラリ作るほどでもないかな?とは思ったのですが、練習の意味も込めて作ってみました。

でも初回リリース時は利用できませんでした

リリースしたはいいものの、結果を見たくてnpm installしたら使えない…… おいおい嘘だろ頼むよと、仕事そっちのけで原因の検証を行いました。いや、仕事はちゃんとしました、はい。 すぐに原因の特定と修正ができたのが幸いでした。

原因は Browserify

調べた結果、ビルドの際 Browserify を使っていたからのようです。 ちゃんと Browserify のことを知っている人からすれば「そりゃそうだ」って感じですよね。

使ってしまった理由としては、他のプロジェクトで TypeScript を使って開発していたときの package.json を流用したからです。 コピペがまずいほうに影響してしまった結果です。反省してます。

ちゃんと export していたにもかかわらず、そのビルドのタイミングで使えなくなってしまいました。

そもそも Browserify ってなんだろう

間違えてしまったものは仕方ないので、そもそも Browserify はどんなことをしているのか見てみましょう。

ブラウザで動かない AltJS をトランスパイルする Babel や TSC(TypeScript コンパイラ)とは違い、簡単に言えば、import,export,requireを解決してファイルを繋げてくれるツール、という認識です。 ただそれだけではなく、プラグインで AltJS をトランスパイルしたりしてくれます。今回使用したtsifyなんかもそうです。 また、ファイルを繋げた後に、グローバルスコープ汚染を防ぐための処理が入ります。 要するに処理全体を即時関数で包むというアレです。

(function () {
    // 処理
})();

今回発生したエラーは、この処理が走ったがために、外部へexportされなかったのではないかと思います。

実際に Browserify でトランスパイルされたJSはどうなっている?

勉強のため、トランスパイルされたコードを見てみます。 簡単にするために、今回のモジュールの一部を抜き出しています。 (実際のソースが見たい方はこちら

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";
exports.__esModule = true;
function insertHTMLBeforeBegin(element, text) {
    element.insertAdjacentHTML("beforebegin", text);
}
exports.insertHTMLBeforeBegin = insertHTMLBeforeBegin;
;
},{}]},{},[1])

↑を見やすいように整形したのが↓です。

処理が整形後の32~38行目に書かれています。 31行目を見ると、無名関数に包まれています。(function (require, module, exports)) 1行目の関数eの第一引数として渡されているようです。

そのため、insertHTMLBeforeBeginを代入しているexports変数が外部から見えないので、このファイルをimportできないということがわかります。

再発防止のために

今回やらかしたことはもうどうしようもないので、今後このようなミスを避けるためにはどうしたらいいか考えてみます。 ぱっと思いつくのは3つありました。

まず、作ったあとにちゃんと検証していれば、こんなことにはならなかったはずです。 時間がない朝にデプロイしたのが問題でした。 デプロイ後に、ちゃんと自分で使えるか検証した上で告知(もしくは今回のように Qiita への投稿)を行うべきでした。

次に、使うツールが何をするのかをしっかりと把握しておくことが大切だと感じました。 今回であれば、Browserify とそのプラグインたち、そして npm, Github, TypeScript あたりです。 特にコードをいじるツールについては、しっかりと理解をしておくことが大切だと思います。 実際に動かしてみたときにエラーとなって動かないとなったときに、どこが原因か特定するのに役立つはずです。

最後に、テンプレートを作っておくといいと感じました。 そもそも他のプロジェクトで使用していたpackage.jsonをひっぱって来たのが問題でした。 ですので、それ専用のpackage.jsonなりなんなりを作っておけばいい話でした。

ということでテンプレート作りました

今回のプラグインで使用したツール等をまとめ、「npmパッケージ用のTypeScript + Browserifyテンプレート」を作成しました。 ご自由にお使いください! ライセンスはCC0なので、著作権表示等は不要です。

github.com

おわりに

みなさんもnpmパッケージ作ってみませんか? 少なくとも自分が不便だから使いたいモジュールを作れば、利用者が1人はいるので無駄にはならないです。 今回みたいに、もしデプロイしたパッケージが利用できなくても、個人開発なら怒られないですし。

あと、Git(Github)でどういうふうに管理するのがいいのか未だにわからないので、もっと経験を積んだり知識をつけたりしたいです。

おまけ:パッケージとモジュールの違いについて

ファイルをまとめたものがパッケージ、requireでロードされるのがモジュールだそうです。

参考↓

better-than-i-was-yesterday.com

Githubのリポジトリ名がnpm-moduleになっていて、「やっちまったなぁ」と若干後悔しています。

OSSのライセンスについて今一度考える

ReactがMITライセンスになりました

9/26にReactのv16.0がリリースされ、MITにリライセンスされました。 以前のような追加の文面もなく、正式なOSSとして使用できます。

github.com

ただし今回の件で、大きな企業が作っている≒安心して使用できるOSSでも、 開発が止まるという面だけではなくて、ライセンスの問題で使用できないという場合があることを再認識しました。 よりOSSの選定に気を向ける必要がありそうです。

具体的なOSS選定方針

基本的には大企業が作っているOSSなら安心ですが、 ただ単に「大企業が作っているから安心」とだけ言える状態じゃなくなったのは事実。

今後どんな基準で選んだらいいのか考えてみました。 ※あくまで個人的な素人基準です。

  1. 大きな非営利団体
    • ライセンス問題が少なそう&開発停止になることがなさそう。
      • e.g. Apache Software Foundation
  2. 大きな企業(ただしFacebookを除く)
    • 少なくとも開発がストップすることはないため。ライセンスに関して縛りがあれば、3以降のOSSを優先してもいいかも。
      • e.g. Google, Microsoft
  3. 枯れている
    • みんな大好きしおしおになったライブラリ。
      • e.g. ぱっと出てこないけど、変更の必要がないくらい小さなライブラリとかはここに当てはまる?
  4. みんな使っているもの
    • 需要があれば誰かが直してくれる。ただし、日本人だけが使っているようなものは基本的にNG
  5. 自分で作ったもの
    • いざとなれば直せる。(直すとは言っていない。)

まとめ

上記はあくまで個人的な感想ですし、むしろフォークして自分(達)でメンテしていく覚悟があるのであれば、どんなOSSを使っても大丈夫かと思われまーす。