JavaScriptで多重ループを抜けるラベル構文について

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

最近、改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用までという本を読んでいるのですが、ES2015も含めたJavaScriptについて本格的に書かれてあって、とても分かりやすいです。

JavaScriptはある程度分かっていたつもりなのですが、それでもこういうまとまった本を読むと、何個かは知らなかった記述がでてきます(ES2015についてはほとんど知らないことばかりですが)。

今回はその中で、2.5.8に記載されていた多重ループを一気に抜けるラベル構文についてメモ。調べてみたら、よくある記述らしいんで、ブログに書こうか迷ったのですが、前に多重ループを抜けるときは匿名関数にして即時実行したほうがいいというようなことを書いてしまったので……。
for文の二重ループを一重ループにする方法 | while(isプログラマ)

で、上記記事に書いた多重ループの抜ける方法をラベル構文で書くと下記のようになる。

変更した箇所は、最初のfor文の前に『kuku』というラベルをつけ、多重ループを抜けるときには『break kuku;』とやるだけ。上で紹介した本が改行してあったので、ラベル指定の後は改行しましたが、『kuku: for(i=0;i<9;i++){』と改行しないほうが分かりやすいかも? このラベル構文はcontinue文でも利用できるとのことで、またswitch文の中でループを抜ける際にはラベル構文でなければならないとのこと(そりゃ、switch命令内でbreakとすると、switch命令を抜けるだけですしね)。 参考:break文とcontinue文でのラベル指定 – 繰り返し処理 – JavaScript入門

改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで
山田 祥寛
技術評論社
売り上げランキング: 4,391

JavaScriptで関数の引数名を取得する方法とその利用例

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

自分が今関わってるプロジェクトですが、AngularJSでサイトを作っていた人が別のプロジェクトに移動することになってとりあえず自分が引き継ぐことになりました。

AngularJSはちょっとだけ触ったことがあるのですが、正直よく分かっていません。Backbone.jsやReactはなんとなく分かるのですが、AngularJSはほとんど全く分かりません。で、案の定、コードを見てどのような動きをしているのか調べようと思っても全く分からず。特に、ある関数(確か、controllerの引数に含めた無名関数)の引数の順番がどうなっているのか全く分かりません。複数のファイルで同じような記述があったのですが、全く順番が異なるように思えました。いくら調べても分からなかったので、仕方なく尋ねると、「Angularが引数名で判断してるんです」。

はぁ? JavaScriptはある程度分かってるつもりの自分ですが、引数名を判断する方法があるなんて知らずに、驚きました。

調べてみると、すぐにその方法が見つかりました。単純に、関数自体のtoStringメソッドを呼んで、分解しているそうです。
参考:関数の引数名を取得する – Qiita

上記のgetParams関数は、引数に関数を入れると、その関数の引数名の配列を返すようになっているようです。replaceの中身の正規表現をよくよく見てみると、一つ目の|の左側(\/\/.*$)が行コメント、二つ目の|の左側(\/\*[\s\S]*?\*\/)が複数行コメント、二つ目の|の右側(\s)がスペースやタブや改行文字などのホワイトスペース文字を表しており、フラグにはg(グローバルサーチ)とm(複数行検索)を指定しているそうです。その正規表現にヒットした文字をまず無くしているわけですね。

続いて、matchを使って、丸括弧内を検索して引数のみを取得しているようです(ちなみに、インデックスを0にするとカッコを含む文字列を取得できる)。

実際にAngularJS 1.5.8のソースコードを調べてみると少しだけ異なるようで、3923行目のextraArgs関数でtoStringの結果を分解しているようですが、replaceの中身の正規表現は『/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg』となっています。カッコが多いのはともかく、この時点ではホワイトスペースを省いていません。さらに、matchも『/^([^\(]+?)=>/』と『/^[^\(]*\(\s*([^\)]*)\)/m』の二種類になっています(前者はES2015の対応っぽいですね)。変数名に)は使えないのだから、『[^\)]*』なんてしなくていいような気はするんですが、念のためそうしているのかもしれません。

なお、実際にextraArgs関数を呼んでいる箇所は3955行目のannotate関数の中のようです。この下のforEach関数(※:念のため言っておくと、この関数はAngularJSで定義している関数です)内のreplaceでホワイトスペースを削除しているようです。replaceの第一引数の正規表現は『/^\s*(_?)(\S+?)\1\s*$/』となっています。どうやら、引数の前後にアンダースコア(_)がついてもいいようですね(ただし、実際に試しとぁけではないので本当にそうなのかは分かりません)。

とりあえず、引数名を取得する方法はわかったので、自分でも実際に試してみることにしました。そこで書いてみたのが以下のようなコード。

第二引数に指定した関数の引数名を取得して、その引数名に対応する第一引数のオブジェクトの値を引数に含めて実行するという関数です。この関数を書くのにあたってこまったのが、どうすれば関数の引数の数を動的に変更できるのかということ。調べてみたら、Functionオブジェクトのapplyメソッドを使えば、配列内の値が引数になって実行されるということが分かって、なんとかできました(参考:Function.prototype.apply() – JavaScript | MDN)。applyの第一引数のthisには特に意味がありません。意味がないのでむしろ、nullとかでもしておくべきだったかもしれません。

というわけでさらにこの関数を実行するサンプルを作成してみました。
サンプル:関数から引数名を取得するサンプル

コードは下記のようになっています。

もう少し実用的なサンプルを作成できればよかったのですが、思いつきませんでした。

それにしても、あらためてJavaScriptは自由度の高いプログラミング言語だと思いしらされました。自由度が高すぎて、AngularJSみたいに、パット見わけがわからないライブラリまでできてしまうという。まあ、AngularJSのせいにするのもなんなので、もう少しAngularJSを勉強しようと思います。

右矢印キーを押すと左にカーソルがうつる不可思議なテキストボックスについて

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

前の会社での話。

実は、前の会社を辞めてからも9月は特別に週一回行っていまして、自分がある程度関わったプログラムのバグ修正なんかをしていました(ほとんど、自分が関わったところではないところに時間を使いましたが)。

そんな中、昨年退職した上司が作成したあるウェブページのテキストボックスが変な挙動をしていました。

そのテキストボックスには、数値が入っていて、右寄せになっていました。その時は特別深く考えたわけではないのですが、CSSのtext-alignでrightを指定しているんだろうなとは思いました。

それはいいのですが、どうも入力するとおかしなことになります。

IEだと右矢印キーを押すと左にカーソルが移動し、左矢印キーを押すと右にカーソルが移動します。よくわからないけど、IEのバグかなと思って試しにChromeで見てみると、なんとChromeでは一番右にカーソルをうつして入力すると左側に文字が追加され、逆に一番左にカーソルをうつして入力すると右側に文字が追加されます。

初めての減少に思わずポカンとしてしまったのですが、多分、keydownでイベントを発生させていて何かやらかしているのだろうと思い調べてみました。すると、予想通り、数値のみしか入力させない記述がしてありました(参考:HTMLのテキストボックスを、できるかぎり半角数値のキー入力のみにする | while(isプログラマ))。

というわけでいろいろ変更してみたのですが、全く変化がありません。そして、試しにkeydownのイベントを無くしてみましたが、全く変化がありませんでした。正直、わけが分かりませんでした。

とりあえず、一から考え直そうと思って開発者ツールで対象のテキストボックスを選択し、右側のサイドバーを見たところ、あると思ったものが無いことに気づきました。そう、『text-align:right』の記述がありませんでした。スクロールバーを上から下まで移動させて見てみたのですが、その記述がみあたりません。「右寄せになってるのになぜ?」もはやパニックに近い状態の自分でしたが、ふと見られない記述がありました。

『direction:rtl』

なんじゃこりゃ? と思って調べてみると、文章の記述方向の設定をしている記述ということが分かりました(参考:direction – CSS | MDN)。rtl は右から左……。まさか……。

この記述を無くしたら、カーソルが想定通りに動きました(もちろん、これだと左寄せになってしまうので、『text-align:right』を追加しておきました)。

とりあえず、同じようなテキストボックスを下記においておきます。valueの値は12345としています。こうすれば、『54321』と表示されるような気がしなくもないんですが、『12345』と表示されています。

というわけで、もし、矢印キーを押したら逆方向にカーソルが移動する場合は、この記述がないか疑ってください。そんなこと、滅多にないと思いますが……。

なお、「テキストボックスに『direction:rtl』の指定があればカーソルの動きがおかしくなる。ということを言いたいだけだろうに、前置きが長すぎ! そんなんだからブログ続かないんだろ!」なんてツッコミは受け付けておりません。むしろ、こう書いたことがセルフツッコミです。

実は、転職しました

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

久々の更新。2月以来なので、半年以上も更新していなかったことになる。

まあ、2月3月は確かに忙しくて更新できなかったのだけど、4月以降はそれほど忙しい日があったわけではないので、更新できなかったわけではないのですが、なぜかブログ更新というのはしばらく更新しなくなると、そのままズルズルと更新しなくなってしまうもののようです。

ブログを開設した当初は「週1回は更新する!」と意気込んでいたのはなんだったのかと。これで、長期間ブログの更新をしなくなったのは何回目なのかと。

この期間にはいろいろなことがありました。HDDが壊れかけていたので、いったんリカバリした後に新しいHDDに換装したりしました。そのせいで、Twitterのサブ垢がJanetterで使えなくなりました(メインで使っているのは、バックアップ時に使っていたから使えた)。そもそも、パソコンが買って5年近くたつので、そろそろ買い換えようかとも思ったのですが、調べてみると買ったときと同じぐらいの性能のパソコンが、当時買った価格の1.5倍程度するので、迷った挙句、辞めました(スペックは下げたくない)。本当、あの時は円高の恩師を受けたいたんだなと今更になって実感しました(誰だ! ムーアの法則によって価格が安くなるとか言ったのは。と思ったけど、ここ数年、ほとんどムーアの法則を実感できていなかった……)。とりあえず、Windows7のサポートが2019年まで続くそうなので、それぐらいまでには買い換えようと思う(なぜ、10にアップデートしなかったかというと、テレビ機能が使えなくなるとのことだったから)。

それはともかく、5月のゴールデンウィーク明けのこと。

知人に誘われて、晩御飯にいったんですよ。てっきり、居酒屋かなんかだと思ったら、会員制の高級レストラン。話の内容は会社へのお誘い。つまり引き抜きについて。話を聞くと、悪くなさそうだし、当時働いていた会社に悪い噂があったので、とりあえずオッケーしました。

その後は、面接とかあって、人生で初めて頭が真っ白になって何も答えれなくなったという体験をしたのですが、無事に内定をもらえ、9月1日より現在の職場に働いています。

いま担当していることは、Androidのアプリ開発です。アプリ開発といっても、すでに存在していたプログラムの仕様を変更するだけなので、たいしてむずかしいことはしていません。Androidのアプリ開発は未経験ですが、なんとかやっていくことができました(といっても、部品の配置など、HTMLとCSSの感覚でやっていると思うようにいかないことも多いので、苦労することはあります)。

そんなわけで、また更新再開すると思います。とか言いながら、10月から忙しくなるそうなので、また止まるかもしれません。

ところで、自分は担当ではないのですが、同じプロジェクトのウェブサイト版にて画像をアップロードする箇所があり、さらにその画像はBase64で渡す必要があるということが分かりました。その際、朝会にてウェブサイトを担当していた人が、「Base64にできるか分からない」というようなことを言っているので、「対応のブラウザ(IE11とChrome)ならできますよ」と伝えました。その後、どうせなら参考になりそうなページを探してメールでも送ってあげようと思い、確か、『File API』だったよなと思ってGoogleで『JavaScript File API Base64』と検索すると、このブログの記事(File APIを使ってファイルデータのbase64変換 | while(isプログラマ))がトップにきたのでページを閉じました。会社で調べごとしていてこのブログの記事がヒットするとちょっと恥ずかしくなります。

ExcelでのVLOOKUPの代用(MATCH,INDEXとMATCH,OFFSET)と速度について

[`evernote` not found]
[`livedoor` not found]
[`yahoo` not found]

よく忘れるのでメモ。

知ってる人も多いと思いますが、ExcelにはVLOOKUPという関数があります(参考:VLOOKUP 関数 – Office のサポート)。これは、表の中にある、特定の値(検索値)に対応する値を取得する関数なのですが、この関数は検索値よりも右側の値しか取得できません。最初、第三引数にマイナスの値を指定する右側の値を取得できると思って試したのですが、無理でした。このため、昔は表の右側に、検索値より左側の値を参照する式を追加していていました(検索値がB列でほしい値がA列の場合、C列に”=A列の値”といった式を入力する)。

ただ、最近になって代用方法があるということを知りました。どうやら、MATCH関数とINDEX関数の組み合わせ、もしくはMATCH関数とOFFSET関数の組み合わせで代用可能とのこと(参考:徹底解説(VLOOKUP,MATCH,INDEX,OFFSET)|エクセル関数超技)。

簡単に説明すると、MATCH関数は指定の範囲から検索値が何番目にあるかを返す関す、INDEX関数は指定の範囲内にある指定の行数、列数から値を取得する関数、OFFSET関数は指定のセルと行数、列数から指定のセルの行数、列数分移動したところにある値を取得する関数です。

言葉だけでは分かりにくいと思うので、実際に試した結果の画像を下記に載せておきます。
エクセルで試した結果

F列がG列の式をセットした結果です。検索範囲はA列からC列、検索値はF1にある『近畿』です。見てもらえればなんとなく何をしているか分かるかと思います。F2,F5,F7では『近畿』という値をB列から検索し、その右側の値を取得して『7』という結果を得ています。F4,F6では『近畿』という値をB列から検索し、その左側の値を取得して『5』という結果を得ています。

直感的にはINDEX関数よりOFFSET関数のほうが意味合いが分かりやるいと思っています(INDEX関数って言われても、どういう関数なのかピンと来ない)。ただ、OFFSET関数は第二引数に-1を付けなきゃいけない(1引く必要がある)ので、自分は何度も間違えました。なので、慣れたらINDEX関数とOFFSET関数の組み合わせで対応するのがいいかと思います。

というより、そもそもこの組み合わせを分かっていたらVLOOKUP関数を使う必要はないかもしれません。たとえ探したい値が検索値より右側にあったとしてもINDEX関数とOFFSET関数さえ使っておけばいいんじゃないかと(もちろん、こっちのほうが少し冗長ですが)。

ただ、そこで気になるのが速度の問題です。関数が一つですむVLOOKUP関数にたいして、INDEX関数とOFFSET関数の組み合わせだと関数を二つ使うことになります。ということは、その分、速度も遅くなってしまうのではないかと。

というわけで、検証してみることにしました。用意したのはアルファベットをランダムに3文字並べた文字列50万個をSheet1とSheet2の二つのシートのA列に記載し、Sheet2のほうにはB列に行番号を追記するというもの。その後、式のままだと問題があるので、式を結果で上書きしました(全体をコピーした後、右クリックで値貼り付け)。
参考:アルファベットをランダムに表示したい−CHAR関数・RANDBETWEEN関数:Excel(エクセル)の関数・数式の使い方-文字列

作成後は下記のようになっています。
Sheet1
Sheet1
Sheet2
Sheet2

その後、VBAにて計測処理とSheet1のB列において検索式の追加処理、C1の値に処理を開始してから完了するまでの時間(秒)を記載する処理を追加。具体的には下記のような感じ。

上記はINDEXとMATCH関数の組み合わせのみ記載しましたが、VLOOKUP関数やOFFSETとMATCH関数の組み合わせでもほとんど同じです。5行目がVLOOKUP関数になるかOFFSETとMATCH関数になるかが違うぐらいです。

結果は下記のような感じになります(環境Excel2010です)。
結果

以下、それぞれ5回ずつ試した結果。

VLOOKUP実行 INDEX_MATCH実行 OFFSET_MATCH実行
1回目 78.453125 77.2578125 77.234375
2回目 77.3046875 77.5 77.2734375
3回目 77.359375 77.6171875 77.421875
4回目 77.5390625 77.59375 77.4140625
5回目 77.4296875 77.75 77.3671875
平均 77.6171875 77.54375 77.3421875

ほぼ全く関係なさそうです。微妙に違いますが、誤差レベルと思われます。INDEX,MATCH関数の組み合わせよりOFFSET,MATCH関数の組み合わせのほうがちょっとだけ速いような気もしなくはないですが、気にするほどの差はないかと思われます(VLOOKUP関数の1回目が遅いのは、最初の実行だからかと思われます)。