/bin/osh

ソフトフェアエンジニアリングしたりデータ分析したりプロジェクトマネジメントの勉強したりする人のブログ

Lisp入門

概要

この記事でわかること

  • Lispの基礎知識
  • Lispのコアとなる部分の考え方
  • Lispがなぜすごいのか

この記事でわからないこと

  • Lispでのアプリケーションの作り方
  • もう1歩踏み込んだラムダ計算の考え(別の記事orLTでやります)

入門じゃないじゃん!と言われる前に言っておくと、入門というのは手取り足取り"Hello World"をするものだけではない。
Lispの考え方、Lispの世界に入門する、という意味で"Lisp入門"としている。

Lisp入門

Lispをちゃんと理解するためにLispがどのような思想で作られたのかを調べた。
今回この記事を書くにあたって、Lispというプログラミング言語が提唱された論文
"Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I"を参照した。
この論文が出たのは1960年であり、Lispが考案されたのは1958年だ。
1957年に"広く使われた世界最初の高級言語"と言われるFORTRANが出ているが、それほど古い言語である。
ちなみに論文のタイトルの最後に Part Iとあるが Part IIはない。

Lispの概要

論文の内容の前にLispの基本知識を簡単に説明する。
LISt Processorという意味を持っている。
つまり、Lispの中核はlistなのである。

Lispと聞くと何を思い浮べるだろうか。
おそらく、Lispを少しでも知っている人ならば"かっこ"を思い浮べるだろう。
Lispにとって"かっこ"はとても重要なものだ。
まず、Lispのexpressionは atomlist(pair) から出来ている。
atomとは、"かっこ"と"·"以外の文字が1つ以上繋ったもの(例:foo bar)
listとは、0個以上のexpressionが"かっこ"の中に空白区切りで配置されたもの(例:(foo bar))である。
これがLispを構成する全てと言っても過言ではない。 関数もlistで表されとデータもlistで表される。 Lispでは、関数とデータに本質的な違いはない。 関数もデータである。

Lisp

S式(S-Expressions)

LispはS式(S-Expressions)で表現される。 S式の定義は以下の通りである。

1. atomはS式である。
2. e1とe2がどちらもS式ならば (e1 · e2) はS式

(a · b) という表現はドット対(dotted pair)と呼ばれる。

Elementary S-functions and Predicates

McCarthyはS式を用いて5つのfunctionを定義した。 McCarthyの言うfunctionとは引数の全てが評価されるS式のことを指している。

  1. atom x:xがatomであれば真を、そうでなければ偽を返す。
  2. eq x y:xとyを評価した結果が同じならば真を、そうでなければ偽を返す。
  3. car x:引数にlistをとり、そのlistの先頭を返す。
  4. cdr x:引数にlistをとり、listの先頭以外を返す。
  5. cons x y:xとyのドット対を作る。

これだけあれば様々なlist操作はできるが、プログラミング言語としては不十分である。

Recursive function

先程の5つのfunctionに加えて再帰関数(Recursive function)を書くことができれば、チューリング完全高級言語ができる。 再帰関数を書くために、McCarthyは次の4つを定義している。 (実際にはSUBSTとf[e1;...;en]という表現も同時に定義しているが、コアな部分でないため割愛する。)

  1. QUOTE x:xを評価せずS式自体を返す。
  2. COND (p1 e1)(p2 e2) ... (pn en):条件式(実際はただのS式)とS式のペアの0個以上とり、合致した条件とペアのS式を評価する
  3. LAMBDA (x1 x2 ... xn) e:いわゆるラムダ式。これ自体が関数と同じ働きをし、引数を2つ以上とり、最後の引数はその前の引数をラムダ式の引数とした関数の中身となる。(例:(lambda (a b c) (+ a b c)) (1 3 5))
  4. LABEL x e:eをxで与えられた名前で保持する。

これらの4つがあることによってチューリング完全となる。 実際には不動点コンビネータによってLAMBDAだけで再帰関数が書けるため、チューリング完全のためだけならばLABELはいらない。 McCarthyがこの論文を書いた際には不動点コンビネータが発見されていなかったため、LABELを付けて再帰関数を書いたのだと思われる。

まとめ

Lispの何がすごいか、といえばたった7つ(atom eq car cdr cons quote cond)のオペレータとlambdaでeval関数が書けるところにある。 この論文でeval関数をどう作るかも書いてあるが、この記事の内容とevalの作り方を含めてもたった20ページにしかならない。

また、全てがatomとlistで表現されるためProgrammable programing languageと言われるほど拡張性が高い。 プログラム自体をプログラムできるプログラミング言語なのだ。

この入門記事では、Lispのコアとなるオペレータを紹介してLispの世界に入門した。 途中、ラムダ式不動点コンビネータなどの部分が分かない方もいただろうが、これについてはいつか別の記事で紹介する。

プログラミング初心者に最適な言語ってなんだろう

昔からずっと言われてる議題だけど社内の非プログラマ向けにプログラミング講座をやろうと思った時に迷ったのでもう一度考えてみる。

プログラマに最適な言語とは

今回の前提として社内の非プログラマに教えるというところがある。 教えた人が「プログラムって便利じゃん!」って思ってくれると最高。
単純な反復処理をプログラムで処理したり、定期実行系のbotとか作るところが(とりあえずの)ゴール。
おもしろいと思わせるのも重要。
プログラマの作業で一番多そうなのは、あるファイル(Excel or CSV)を加工して別の形にするとかだと思うのでFile I/Oのしやすさは重要。

候補

  • Ruby
  • R
  • Shell script

Ruby

日本製スクリプト言語

  • メリット
    • 日本語ドキュメントが多い
    • 社内にも書ける人が多い
    • シンプル(Matzさんの言葉を使うなら「自然」参考:Rubyとは)
    • File I/Oも単純
  • デメリット
    • 環境構築のコスト(非プログラマにとってコストが高そう)

R

統計解析向けプログラミング言語

  • メリット
    • ?コマンドやdemoコマンドの充実
    • データフレームがExcelをよく使う人にとっては強い見方に
    • CSVなどをデータフレームとして読み込める
    • 環境構築が楽
    • RStudioで作業することになるのでエディタを用意しなくていい
  • デメリット
    • googlabilityが低い
    • 日本語のドキュメントが少ない
    • 英語アレルギーだと?demoコマンドが読めない

Shell Script

シェル芸

  • メリット
    • 数個のコマンドを覚えるだけで非プログラマの生産性向上はできる
    • リダイレクト等があるのでFile I/Oも楽
  • デメリット
    • 黒い画面アレルギーの人がいそう
    • Windowsのことを考えると憂鬱になる

まとめ

環境構築さえなんとかなればRubyがいいかなあ
環境構築の面ではRが楽なんだけど英語とgooglabilityの壁がある
リモートにRubyの環境整えておいてCloud Shellとかでssh接続するのはあり。

ChainerRL Quickstart Guideを和訳してDQNしてみた

ChainerRL

2017-02-16に公開された強化学習(Reinforcement Learning)のアルゴリズムをまとめたPythonのライブラリです.

Quickstart Guideも用意されています.

(布教+英語の勉強目的で)このQuickstart Guideを和訳してみたので公開してみます. Dropbox Paperに飛びます. ChainerRL Quickstart Guide JP

英語おかしかったら言ってください…

Quickstartしてみた

Quickstart Guideを参考にこんな感じにコード書いてみたら動いた. 16行目の env = gym.make('CartPole-v0')でOpenAI gymにある別のゲームにしてあげれば別のゲームを学習することができます. ちなみにCartPole-v0は棒のバランスを取るゲームです. python quickstart.py --trainで学習,python quickstart.py --load PATHでエージェントをロードして実行できます.

gist1a7f0f9e6d2dfccb2b7d2b9f3ba0235e

【Unity】Photonを使ってネットワーク間同期

やっぱりゲームを作る上で通信は欠かせない。

サーバー借りるお金はないけど通信したい!っていう人にオススメなPhotonを使ってUnityで位置同期を目標に作ってみる。

Photonってなに?

Photonはさまざまなゲーム開発環境で使えるネットワークエンジンです

photoncloud.jp

簡単に言うとオンラインゲームの通信機能をやってくれるやつ。

Photonの中にも種類があるのですが今回は簡単にできるPhoton Realtimeを使っていく。

Photon Realtimeはチャットやロビー機能、ルームの作成やマッチングなどが簡単に実装できる。

同時接続数20人までは無料!

 

登録とPUNのインポート

登録はここから。

www.photonengine.com

 

登録が終わると、こんな画面になると思うので赤枠の所のアプリケーションID(AppId)をメモっておく。  

f:id:ushiumi:20150928220705p:plain

ここからはUnityでの作業。

自分はUnity 5.2.1f1 Personalで行っています。

Asset Storeで「Photon Unity Networking Free」(PUN)をインポート。

f:id:ushiumi:20150928234412p:plain

インポートが終了するとPUN Setupウィンドウが出ると思うので、

そこに先ほどメモしたAppIdを入れる。

f:id:ushiumi:20150928234700p:plain

すでにUnityで表示されているかもしれないが、

 Assets/Photon Unity Network/Resources/PhotonServerSettings を開き、

Inspector上からRegionをJpに変更し、Auto-Join Lobbyにチェックを入れる。

(それ以外だとラグが大きくなる)

f:id:ushiumi:20150929033726p:plain

 

 Unityにはデモのシーンが出ていると思うが今回は触れないが簡単に2つのスクリプトの紹介。

RandomMatchmaker.cs -> ルームを作ったりマッチングしたりしてくれる。

(起動するとちょっとキモいキャラが出る。)

InRoomChat.cs -> チャットできる。

 

ルームの作成

まずルームを作らなきゃ始まらない!ってことでルームの作成。

とりあえずHierarchy上から右クリックしてCreat Emptyで空のオブジェクトを作る。

名前はPunManagerに

f:id:ushiumi:20150929001541p:plain

 

次にPunManagerのInspectorからAdd Component -> New Scriptで新しいスクリプトを作成。名前はRoomMaking.csにしておく。

これだけでルームの作成は終了。簡単!

 

オブジェクトの同期するための設定

これだけだと通信してる感がない。

適当なオブジェクトを生成させて同期してみよう。

Hierarchy上から右クリックして3D Object->Cube でCubeを作成。

名前はCubeのままにしておく。

(本当はPlayerにしたかったがPlayerだと工事現場のおっさんが出てくる。)

f:id:ushiumi:20150929034117p:plain

ここからが重要でAdd Componentから

Photon ViewPhoton Transform Viewを追加

下の画像のように設定する。Photon ViewのObserved Componentには、

Photon Transform Viewの名前の辺りをドラックアンドドロップして入れておく。

f:id:ushiumi:20150929020901j:plain

Photon Viewは同期させたいオブジェクトにアタッチする。これがないと同期しない。

Photon Transform Viewはその名の通りTransformを同期させるためにつけるのだが、

Interpolate Optionの欄でLerp(線形補間)を指定していることが重要で、これがないと同期ズレが激しくガクガクする。

 

こうして同期するオブジェクトを作り終わったら、

Project上からResourcesフォルダを作りそこに今作ったオブジェクトを入れる。

Resourcesフォルダだけはこの名前でないといけない。その理由は後で解説。

f:id:ushiumi:20150929034204p:plain

 

Cubeを動かすためのスクリプトを作る

この後Cubeを動かさないと検証できないので地面とCube動かすためのスクリプトを作る。本題ではないので簡単に説明する。

こんな感じで地面を作り、(全部白だと見にくいので赤くした)

f:id:ushiumi:20150929022107p:plain

f:id:ushiumi:20150929022258p:plain

Cubeを矢印キーで動かすためにこんな感じにしておく

f:id:ushiumi:20150929023730p:plain

これで再生してみるとCubeをごろごろ転がせる。

ここでCubeをHierarchy上から消しておこう。

 

同期するオブジェクトの生成

同期させるまで後ちょっと。

まずCubeにアタッチされているPlayerMoveのチェックを外す。

f:id:ushiumi:20150929025137p:plain

次に先ほど作成したRoomMaking.csを編集する。

ここで編集したのはOnJoinedRoom()の中だけだ。

普段オブジェクト生成させる際は

Instantiate(Object original, Vector3 position, Quaternion rotation);

こんな感じのInstantiateを使うが、今回はルームに入っている全員の画面に生成させなければいけないため

PhotonNetwork.Instantiate(string prefabName, Vector3 position, Quaternion rotation, int group);

を使う。

第一引数がプレハブの名前(string型)となっているが、これはResourceフォルダの中から探すことになっている。そのため生成させたいオブジェクトはResourceフォルダに入れなくてはならない。

その下の一行は先ほどのPlayerMoveのチェックを外したことにつながってくるが、

ここで自分のPlayerのPlayerMoveを有効(チェックを入れた状態)にしている。

こうしないとキー入力した際全てのPlayerが同時に動いてしまう。

 

実行

これで一通り終了。このシーンをセーブし(今回はtest.unityとした)、動かしてみる。

試すためには1度ビルドをして実行形式にする必要が有る。

File -> Build Settingsと進み、上のScenes In Buildにtest.unityだけがチェックされていることを確認しビルド。

f:id:ushiumi:20150929031031p:plain

こんな感じになりました。

左のウィンドウの時期は動いてるやつ、右のウィンドウの時期は真ん中で止まっているやつ。

 

 

まとめ

とても簡単に同期ができた。ほぼコードを書いていない。

ただ、やはりズレがあるなーって感じ。

Photon Transform ViewでSyncronize Rotationをやっていないので回転が同期されていないのが原因ではあるかも。

それを抜きにしても完全同期は無理がありそう。

でも、お手軽に作れて通信の挙動を確認できるからその点ではすごい!

ちなみにモバイル端末で利用するにはPhoton PUN+というPUNの有料版($95)じゃないといけないみたいです。

今回紹介した機能以外ではRPCって機能をよく使う。

またそれについて書くかもしれない。

今回作成したプロジェクトはGitHubに上げておいた。

github.com