まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2016/02

JavaScript の似て非なる2つの関数: setTimeout と setInterval。この2つの違いは分かりますか?

まず、どちらの関数も第一引数が実行する関数、第二引数が実行までの時間(ミリ秒)、という同じようなパラメータで使います:
<!-- setTimeout の場合 -->
<html>
<head>
<title>setTimeout</title>
<script>
var cnt = 0;
function func(){
  cnt ++;
  console.log( "cnt = " + cnt );
  setTimeout( "func()", 1000 );
}
</script>
</head>
<body load="func()">
</body>
</html>
<!-- setInterval の場合 -->
<html>
<head>
<title>setInterval</title>
<script>
var cnt = 0;
function func(){
  cnt ++;
  console.log( "cnt = " + cnt );
  setInterval( "func()", 1000 );
}
</script>
</head>
<body load="func()">
</body>
</html>

上記2つのページの違いは事実上一箇所です。上は <body> のロード時に setTimeout を、下は <body> のロード時に setInterval を実行しています(そして繰り返し呼ばれます)。この2つがどのような挙動の違いになるか、わかりますか?

説明しやすいのは上の setTimeout を使った場合です。こちらは最初のロード時にまず func() 関数が呼ばれ、 その中で setTimeout を使って1秒にもう一度 func() 関数が呼ばれ、・・・ という具合に、1秒おきに func() 関数が呼ばれるような処理になります:
2016202901


一方、ちょっとわかりにくいのは下の setInterval を使った場合です。こちらは最初のロード時にまず func() 関数が呼ばれ(ここまでは同じ)、その中で setInterval を使って1秒に func() 関数が呼ばれるよう指定されています。そして1秒後に func() 関数が呼ばれると、再度1秒毎に func() 関数を呼ぶよう指定されます。そのため更に1秒経過すると、最初に指定された1秒おきの func() と、2回目に指定された1秒おきの func() と、都合2回 func() 関数が(ほぼ同時に)呼び出されることになります。そして2つそれぞれの func() の中で更に1秒毎に func() 関数を呼ぶよう指定されます。ということは更に1秒経過すると、最初に指定された1秒おきの func() と、2回目に指定された1秒おきの func() と、3回目に指定された1秒おきの func() が2回、都合4回 func() 関数がほぼ同時に呼び出されることになります:
2016202902


この違い分かりますか?つまり setTimeout は次の1回の実行タイミングを指定しているのに対して、setInterval は何秒おきに実行するのかを指定している、ということになります。既に何秒おきに実行するのか設定されている状態で上書きしても、その前の指定がキャンセルされるわけではなく上書き(マルチスレッドのスレッドが1つ増える感じ)で実行タイミングがセットされるので、上記コードのようにループの中で何度も setInterval を実行すると、どんどん実行回数が増えていくことになる、という点に注意してください。

なお、setInterval を停止するには clearInterval 関数を実行します。


※(注)最初にお断りしておきます。マジメぶって書いてますが馬鹿エントリです。


全てのきっかけは最近たまにみかけるこういった記事でした:

人工知能やコグニティブエンジンと呼ばれる技術の発達により、これまで人間の脳でないと判断できなかったようなことをコンピュータができるようになり、人間の仕事がより低コストな人工知能やそれらを搭載したロボットによって奪われてしまう時代がくる、という啓発記事です。

個人的にはそこまでそんな時代が身近に迫ってきているとは思っていません。ただし、その一方で企業間の競争が働いたこともあり、ここ数年における人工知能分野の発展はすさまじいものがあります。静止画像や個人の機械認識率はぐんと上がり、各社が API を公開している背景も手伝って、人工知能に触れる機会がより身近な世界になりつつあるのも事実だと思っています。以下は個人的見解ですが、クリエイティブな仕事(例えば小説を書く、など)を学習させるのはまだ難しいにせよ、脳を使わない単純作業や、ルーチン化された業務などは意外と早い段階で人間の効率を追い抜く日がやってくるかもしれない(そうなると作業コストで勝てるわけがないので、本当に仕事を失う日がやってくるかもしれない)、と思っています。

例えばお客様とお話ししていて、ただ頷いて聞いているだけ。お客様が話し終わったらすかさず相槌を打つ、そんなフローチャートのような業務では近い将来に職を奪われてしまうかもしれないのです!
(注 実在する誰かのことではありません)
2015022305


さて話は変わって、先日秋葉原でこのような部品を買ってしまいました:

2015022300



この SEN02281P はいわゆる「音センサー」です。画像左上にある大きな丸い部分がマイクになっていて、ここで音を拾って、その情報を電気回路を通じて外部に知らせることができる、というものです。買った後で知ったのですが、Arduino に接続したり、Raspberry PiGrovePi という拡張モジュールを取り付けて簡単に使う方法がネットなどで紹介されていました。

・・・ん、もしかすると、この SEN02281P と(例えばラズベリーパイとかの)演算機能を使えば、上記のような簡単なデータフローが実現できてしまうんじゃないだろうか? つまり「誰かが喋っている時は頷き、喋り終わったら相槌を入れる、という仕組みは、このセンサーとアルゴリズムを実装するプログラムだけで実現できてしまうんじゃないだろうか?」ということに気付いてしまったのです! というわけで、よく調べずにとりあえず買ってしまいました。


自分は「ラズベリーパイならメジャーだからまあ繋がるだろう、その方法もネットで見つかるだろう」とタカを括っていたのですが、これが意外と苦戦しました。結論からいうと GrovePi を使わない方法(ラズベリーパイの GPIO に直接繋げる方法)を見つけることができませんでした。えーマジで!?自分で調べるしかないの??電子回路は苦手なんだよなあ。。まあ挑戦してみました。以下、やってみたことのおさらいの意味で書いてます。当方こっち方面はド素人なので、間違いを見つけたり、こうするともっといいよ、という方法があればウェルカム、というか教えてください。

・・・改めてパーツを眺めてみました。接続端子はこの↓画面上部の白い四角の中に生えた4つの突起部分です:
Loudness_101020063_01

これを裏返すとこんな感じ(上図とは左右が逆になった状態):
2015022301


拡大するとこんな感じ。ちょっと見難いのですが、この画面の左から順に(反対から見た場合は右から順に) SIG / NC / VCC / GND と書かれています:
2015022302


GND はアース(Ground)、VCC は電圧、これは(他の部品とほぼ共通なので)分かる。上記の商品ページを見ると、このパーツの動作電圧は 5V(3.5~10V) と書かれているので、とりあえず 5V の電圧ピンにジャンパケーブルを繋いであればいいかな。そして SIG はシグナル、つまりここを GPIO27 とかに繋げて音の信号を受け取るんだろうな・・・ で、NC ?なんだこれ、見たことないぞ・・・

で、ここだけ調べて分かったのは NC = Not Connected 、つまり「どことも繋がない」という端子らしい。そうなんだ。。じゃ、なんで存在してるんだろ?? うーん・・・まあ、いいやw

というわけで、ジャンパケーブルを使ってこんな感じで自分のラズベリーパイの GPIO に接続しました:
2015022303

これで 5V の電源を供給し、アースも備え、SEN02281P が感知した音を取り込む仕組みが動くはずです。実際の写真はこんな感じです(メス-メスのジャンパーケーブルがあればもっと綺麗に接続できたのに・・・):
2016022400


そして、ラズベリーパイ側にはこのような Python プログラムを導入しました:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# SEN02281P ----- RaspberryPi GPIO
# =SIG ---------- 13
# =NC
# =VCC ---------- 2
# =GND ---------- 20

import paho.mqtt.client as mqtt
import time
import RPi.GPIO as GPIO

def on_connect(client,userdata,flags,rc):
	print( "Connection with result code " + str(rc) )
	client.subscribe( "sen02281p" )

def on_message(client,userdata,msg):
	print( msg.topic + " " + str(msg.payload) )

def reading(sensor):
	sum = -1 
	if sensor == 0:
		sum = 0
		for i in range(0,20):
			time.sleep(0.1)
			a = GPIO.input(SIG)
			sum += a
	else:
		print "Incorrect function."

	return sum

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
SIG = 13
GPIO.setup(SIG,GPIO.IN)

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

client.connect( "iot.eclipse.org", 1883 )
while client.loop() == 0:
	msg = reading(0);
	client.publish( "sen02281p", msg, 0, True )
	pass

GPIO.cleanup()
※事前に pip install paho-mqtt をして、Paho の Python ライブラリを導入済みです。

ちなみに上記ソースファイル(sen02281p.py)はこちらからダウンロードできます:
https://raw.githubusercontent.com/dotnsf/sen02281p/master/sen02281p.py


GPIO の(SEN02281P の SIG 端子とつながっている)13番ピンからのインプット情報を 0.1 秒ごとに20回(つまり2秒間)取得し、その20回中音が確認できた回数を MQTT ブローカー(iot.eclipse.org)に投げる、というものです。つまり MQTT ブローカーに対しては2秒おきにどれくらい音が識別できたかを 0 から 20 の整数値でパブリッシュする、というものです。トピックは上記例では "sen02281p" と指定していますが、皆さんがもしこのソースを使う場合は少し変えていただけると嬉しいです。

次に Bluemix 上の NodeRED 環境を使って、このパブリッシュされたメッセージを取り出す仕組みを用意します。MQTT インプットノードを用意し、ホスト名に上記の MQTT ブローカーホスト(iot.eclipse.org:1883)、トピックに "sen02281p" を指定しています。このノードからは2秒に1回、ラズベリーパイの接続された SEN02281P のマイクから拾った音の頻度が渡されてくる、という仕組みとなります。また、その取り出した結果を /ws/unzk_sensor というパスの WebSocket に出力しています:
2015022304


後はこの /ws/unzk_sensor からリアルタイムにデータを取り出して動く WebSocket アプリケーションを用意してあげればマイクで拾った音の頻度をリアルタイムに可視化するようなアプリケーションを作ることができる、ということになります。そのサンプルアプリ(やその中で使う画像)も合わせてこちらで公開しておきます:
https://github.com/dotnsf/sen02281p


このアプリを上記の NodeRED 環境にデプロイしてアプリケーション(hoho.html)を開くと、このような画面になります:
2016022401


ひたすら「頷いている」画面になっています。何もデータが送られていないとただ頷いているだけですが、一応この画面が出ればデプロイには成功していることになります。

ではラズベリーパイ側のアプリも起動します。ネットに接続されたラズベリーパイ上で先程の MQTT パブリッシャーアプリを実行します:
# python sen02281p.py

そして先程の頷き画面をリロードすると・・・ ラズベリーパイに接続されたマイクが音を拾っている間は頷き、音が途切れたと判断した時に「ほほー」と相槌を売ってくれるようになります!
2016022402


この「音を拾っているか」「音が途切れたか」の判断がまだ少し甘いところがあるかもしれませんが、一応それっぽく動いていることが確認できました。これで忙しい営業さんに変わってお客様のお話しを上手に引き出してくれるロボットができました(笑)。


実際に動いている動画を Ustream に上げておきます:

Live streaming video by Ustream




自分は学生時代に初めてパソコンを購入しました。NEC PC-9801 DA です。Intel の CPU が 32bit に対応した直後のモデルでした。

当時からプログラミングはしていました。が、どちらかというとまだ「授業で使うため」「単位を取るため」という理由でした。でもそれが20数年後の今の自分の礎になっているのかと思うと感慨深いです。

さて、その当時に流行っていたテキストエディタの1つが MIFES (マイフェス)でした。1985年にメガソフト株式会社から PC-9801 の MS-DOS 環境向けに発売されたこのテキストエディタは強力な検索機能と、マクロ言語によるカスタマイズ性の高さが特徴でした。

と、偉そうなことを言ってますが、実は自分自身は友人の環境を触らせて使わせてもらっていたのでした。本音としては「当時は値段が高くて学生身分の自分には手が出せなかった」のでした。当時のテキストエディタの2大勢力の、もう1方であった Vz (こっちも1万円近くした記憶が・・・)を愛用しており、実は今でも PC-DOS の仮想環境では使っていたりします。

さて、Vz は Windows 環境では Wz エディタとなって進化を続けていますが、MIFES もずっと進化を続けてきました。MIFES の現在の最新バージョンは10で、先日30周年を迎えたようです。おめでとうございます。

そして、なんと MIFES の体験版がダウンロードして使えること、しかも Linux 版が存在していたことを、恥ずかしながらつい先日知りました。この Linux 環境で使えるテキストエディタという意味では Vz/Wz にはなかった環境です。さっそく試してみました。

まずはここから MIFES for Linux をダウンロードします。自分が試した時は milinux-103.tar.gz というアーカイブファイルがダウンロードできました:
http://www.megasoft.co.jp/cgi-bin/try/milinux/cgi/form.pl


なお、動作環境ページに制約事項が書かれています。自分が以下で試した環境は 64bit の CentOS 6.6 で、「64 bit OS の動作保証はしていない」と書かれていますが、一応以下の手順で動いていました、ということを記載しておきます。

この MIFES for Linux を 64bit 環境下で動かすには ld-linux.so2 ライブラリが必要でした。なので、こいつをあらかじめインストールしておきます:
# yum install ld-linux.so.2

で、その後にダウンロードしたファイルを展開すると mifes というフォルダが作られ、その中に一式のファイルが含まれています。これでインストール完了です:
# cp milinux-103.tar.gz /opt
# cd /opt
# tar xzvf milinux-103.tar.gz
# mifes

ここで1点注意が必要です。MIFES for Linux がサポートする日本語エンコードは EUC のみ、ということです。なのでログインに利用している端末(例えば PuTTY や TeraTerm)の日本語設定を EUC に変更する必要があります。以下は TeraTerm の場合の変更内容です(メニューから「設定」-「端末」で、この画面で文字コードを EUC にしています):
2016022001


ここまでの設定をした上で MIFES for Linux を起動します。コマンドは mi で、ファイル名を引数に指定してもいいのですが、とりあえずそのまま起動してみます:
# ./mi

すると DOS 時代の懐かしいテキストエディタインターフェースが Linux の端末上で再現されます。まずはファイル選択画面が開きます:
2016022002


ここで矢印キーを使ってディレクトリを変更しながらファイルを選択します。例えば mi と同じディレクトリにある license.txt を選びました。このファイルを開くには選んだ後に Enter を押します:
2016022003


MIFES 独特の、青い背景に灰色文字で選択したファイルを開くことができました。当時のユーザーには死ぬほど懐かしい「ダイヤモンドカーソルキー(Ctrl+E,S,D,X がそれぞれ矢印キーの上、左、右、下に対応して、ホームポジションに指を置いたままカーソル移動ができるショートカット)」も再現されています。使っていると指が思い出してくれます:
2016022000


メインメニューは F1 キーで、そこから別のファイルを開いたり、終了したり・・・といったオペレーションが可能でした。


唯一気になった点を上げるとすると、やはり UTF-8 エンコードに対応していない、という所でしょうか。今自分の手元にあるソースコードファイルはほぼ UTF-8 で作られており、それらが読み込めないというのはやはり痛いです。とはいえ、今後 MIFES for Linux の進化が期待できるかというと・・・ まあ難しいということを理解しないといけないのだと思っています。

しかし学生時代を含めても、自分の環境下に MIFES をインストールしたのは数えるくらいしかなかったし、それを Linux で行える日がやってくるとは思っていませんでした。

StrongLoop の API プラットフォームにおける利用状況や接続先データソースをグラフィカルな UI で管理するツールが StrongLoop Arc です:
https://strongloop.com/node-js/arc/


StrongLoop (や LoopBack )の slc コマンドを導入済みの環境であればすぐに使うことができます。試しに LoopBack で API を作った時の環境を使って、StrongLoop Arc を利用してみます。LoopBack を導入する手順についてはこちらを参照ください:
CentOS に StrongLoop をインストールする


上記手順を行い、"myapp2" という名前で LoopBack アプリケーションが作られていると仮定します。この環境を使って、StrongLoop Arc を利用するには、アプリケーションフォルダに移動して、"slc arc" コマンドを実行します:
# cd myapp2
# slc arc

するとこんな感じで StrongLoop Arc が起動します。以下の例では 41241 番ポートで起動していますが、このポート番号は動的に変わるようです:

2016021810


ではこの環境にウェブブラウザでアクセスしてみましょう。なお、StrongLoop Arc はリモートアクセスを許可していないので、この StrongLoop 環境を導入したマシンそのものにログインしてからウェブブラウザで http://localhost:XXXXX/ にアクセスする必要があります:
2016021801


StrongLoop Arc を使うにはアカウントが必要です。まだお持ちでない場合は画面右上のハンバーガーメニューから "register" を選んで登録を行います:
2016021802


登録するユーザーのメールアドレスを指定して "Submit":
2016021803


指定アドレスに StrongLoop からメールが届いたら、メール内のリンクをクリック:
2016021804


で、ユーザー名やパスワードやら、残りの項目を登録します:
2016021805


最後に同意チェックを入れて Submit !これでアカウント登録完了です:
2016021806


改めてこの画面から登録ユーザー名(またはメールアドレス)とパスワードを指定してログインします:
2016021807


ログイン直後の画面がこちらです。コマンドラインの slc コマンドで行っていたような作業を GUI で行えるようになります:
2016021808


試しにデータソースを管理してみましょう。"Composer" をクリックすると、現在(LoopBack を使った時に登録した)データソースが確認でき、またここから新規にデータソースを定義することもできるようです:
2016021809


プロセスマネージャーを登録してトレーシング・・・ などもやってみたかったのですが、何故か上手く行かなかったのでそちらについてはまた別途、かな。


(2016/Dec/02 追記 このエントリ内で紹介しているページが消えてしまったため、具体的なサーバーを確認することができなくなってしまいました)


このブログでもたびたび紹介している Loopback などを提供している StrongLoop 社の技術者向けブログを見
ていて気付いたことがあります。

気付いたのはこのブログエントリ、LoopBack から MySQL データベースに接続するための手順を紹介しているガイドです:
Getting Started with the MySQL Connector for LoopBack

(2016/Dec/02 追記 ↑このリンク先ページが消えてしまいました)



ページの中ごろに差し掛かったあたりで・・・ ん!?
2016021801
 (↑実際にはモザイク無し)


LoopBack の MySQL コネクターを使ったデータソースを記述する手順を紹介している箇所で、StrongLoop がデモ用に公開している MySQL サーバーの接続情報が載っているではありませんか!?

まあデモ用なので、このユーザーでできる権限は問い合わせ(つまり SELECT)程度です。とはいえ、そんな MySQL が公開されているとな!

早速 Bluemix 上に phpMyAdmin 環境を構築してみました(笑)。その手順についてはこちらを参照ください:
Bluemix で phpMyAdmin を動かす


1つ注意点として、StrongLoop のデモ用 MySQL はバージョンが低いらしく(僕が確認した限りでは 5.1.73)、最新 phpMyAdmin の推奨環境に合わないためか、そのままだと「バージョンアップしろ」という内容のエラーが表示されました:
2016021800


このエラーを(無理やり)回避するには、phpMyAdmin 側のエラーチェック部分を変更する必要があります。phpMyAdmin の libraries/common.inc.php ファイルの下記該当部分を全てコメントアウトして保存します:
   :
:

# if (PMA_MYSQL_INT_VERSION < $cfg['MysqlMinVersion']['internal']) { # PMA_fatalError( # __('You should upgrade to %s %s or later.'), # array('MySQL', $cfg['MysqlMinVersion']['human']) # ); # }

:

その上で phpMyAdmin 一式を(Bluemix 環境に)デプロイして phpMyAdmin にアクセスし、上記ブログ内で公開されている ID とパスワードでログインすると、デモデータベースの中身にアクセスできました:
2016021802


定義や内容を変更することはできないのですが、動作確認用のサンプルデータベースとして MySQL データベースサーバーが必要な場合に重宝しそうです。


(参考)
http://stackoverflow.com/questions/26222244/phpmyadmin-error-you-should-upgrade-to-mysql-5-5-0-or-later


このページのトップヘ