ゆずとみかんといちご

ゆるゆる書きます

Play Framework on IntelliJ

備忘録

仕様

仕様は上記の通り. 1. IntelliJ が Play に対応しているのは Ultimate 版のみ.お金を払いたくないので Student 会員になった. 1. Play 2.4.x は JDK 1.8 に対応しているが,IntelliJ 14 は JDK 1.7 推奨. 1. 現段階で IntelliJ の最新版は 15.15 (Ultimate) だと Play 2.4.6 で JDK 1.8 でいけるはず.

IntelliJ IDEA を使って Play Framework を使う

Play のドキュメントを見ると,2.3 までは IntelliJ IDEA から Play application を作成できたが,2.4.x では非推奨になっている.

  1. コマンドラインから activator new NewProjectName で新規プロジェクト作成
  2. IntelliJ IDEA 内で Open よりNewProjectName を開く
  3. sbt の設定画面が出てくるがとりあえず JDK のバージョンを揃えたくらいしかしていない
  4. File ->Project Structureを開き,ProjectSDKs` で JDK のバージョンを揃える
  5. Run -> Edit Configuration を開く.+ -> SBT Task で name は適当 (今回は Run),task を run として登録
  6. Run コマンドが作られるので押すとサーバーが立ち上がる (今回はローカルの9000)

日本語ドキュメントを求めて Play 2.3.x のものを読むと死ぬ Document for Play 2.4.x:

IDE

ocaml-alab-mode もどきを作っているお話

この記事は 学生エンジニア Advent Calendar 2015 21日目の記事です.

この間このカレンダーを落とさないために今日の分のネタを投下してしまったので今日はおそらく一部の界隈にしか受け入れられないであろうお話をします (かなしい).

Agenda

  1. agda2-mode で TDD をした
  2. 上の経験により TDD 初学者の心を取り戻した
  3. OCaml ver. を作った

界隈の方にはふわっとしすぎたお話ですし,そうでない方にとっては聞き慣れない単語の羅列になっていると思います.頑張って動画とか載せてイメージしやすくしていくのでどうか諦めないでください…

agda2-mode

Agda というプログラミング言語がありまして,Coq を知っている方は聞いたことあるかなと思うのですが,定理証明をするための言語です.Coq との違いはここでは触れませんが,Coq よりも プログラミングしているっぽい 感覚で定理証明をすることができます.Haskell を書いたことがある人は,あれに依存型が入ったような感じと思っていただければ.そう,依存型.Dependent Type.

依存型 - Wikipedia

一言でいうと (たぶん怒られそうなのですが) 「型に依存した定義を持つ型」というわけです.依存型の説明でおそらく一番有名なのは「安全な head 関数を作る」だと思います.@yoshihiro503 さんのこのブログなど分かりやすいです (これは Coq ですが).

Coqで依存型 - にわとり小屋でのプログラミング ブログ

分からなくても大丈夫です.今回は依存型の話はしません.できません.とりあえずおそらく皆さんが普段イメージする「型」の概念よりももう数段神様の世界に近づく概念だと思ってください.

で,この Agda の授業が大学院の授業であったんですね (講義録).

このとき私は Agda を書いていたというよりも agda-mode (agda2-mode) というものに Agda を書かされていたわけです.

github.com

これは Emacs のモードの一つで,Agda を書いているときにプログラム中に expression として ? (hole) を書くことができ,その部分の型とそのときの型環境を表示してくれるものです.あと hole 内に書いた変数で case 式 (pattern match) とかを展開してくれます.とにかく agda2-mode が言った通りの型 (の値) を型環境にあるものを使ってうまく作って hole に入れてあげればいいわけです.

言っても分からないと思うので見てください.下のウィンドウに hole の型が表示されています.今回コマンドを押し忘れたのですが,hole のところで C-c C-, とすると hole の型だけでなくスコープ内にある変数の型一覧 (型環境) も表示してくれます.

youtu.be

超便利!!型合わせゲーム最高!!!TDD (Type Driven Development)!!!TDD (Type Driven Development)!!!

ちなみになんと最近流行りのモダンなエディタ Atom にもこの agda-mode があるので宗教上の理由により Emacs を使えない方はこちらをお試しください.

agda-mode

実際に使ってみた私のらぼの同期の記事はこちら

Atom で Agda - Qiita

プログラミング初学者の心

当時私は大学の後輩になんとか OCaml の可愛さを理解していただきたく,先輩が実装された型デバッガというものを初学者向けに改良する,ということをしていました.この型デバッガはコンパイラ型推論器を使ってプログラム中の型エラーの原因を特定していくデバッガなのですが,対話的である という点が画期的なところ (の1つ) です.型デバッガはユーザに,例えば「ハイライト部分にある変数 x の型を int だと意図しているか?」と (n >= 1 回以上) 尋ねます.ユーザはこの問いに対して yes, no で回答していくだけでどこで型 (推論) が conflict しているのかを見つけ出すことができます.要するに,コンパイラが推論した型とユーザが意図している型がどこで食い違っているかを探索していく,というものです.

こちらも百聞は一見に如かずです.以下では見かけは同じ2つのプログラムの型エラーの原因が,ユーザが意図している型によって異なる例です. (ちなみに以下では OCaml 3.12 になっていますが 4.01 まで対応しています)

youtu.be

おかげ様で型デバッガを採用した OCaml の授業を受けた学年は「C より Java より OCaml が好き」と言ってくれるまでに OCaml を愛してくれました.めでたしめでたし……

というわけにもいかなかったのです.

弊大学の OCaml の授業では TDD (言わずもがな T は Type の T) を最初から導入していて,関数を書く前にコメントアウトでその関数の型を書かせるようにしています.例えば「リストの中の整数全てに与えられた整数 p を足したリストを返す関数 plus_p」なら

(* plus_p: int list -> int -> int list *)

というのが全体像となります.これを考えさせてから実際に plus_p 本体の実装をするようにしています.これを考えるとき,自然とアルゴリズムや他の関数のことやモジュールの設計なんかについてなんとなーく考えるようになりますから.それからこれがしっかり書いてあると他の人が読みやすいのです.なんていったって型はドキュメントですので.

…でもさっきのあれ,コメントアウトなんですよね.

だから適当なこと書く人も割といて,最初に全体像を把握出来ていない,ドキュメントを考えられていないからプログラムの中身をどう書いていいのか分からない,そんな """負 (不)""" があったわけです.

前置きが長くなりましたが,そんなわけでさっきの agda2-mode の OCaml ver. を作ろうと,そう,思ったのです.

ocaml-alab-mode

名付け親は私です.alab は弊研究室がこの界隈では「A研」で通っていて,弊大学の OCaml の授業で使う OCaml のサブセットについては網羅していこう,というところから alab-mode となりました.あと現段階では Emacs でしか使えません.agda-mode for Atom が悔しかったので時間できたら Atom 用のパッケージでも作ります.Vim 用は Vimmer のどなたかにお任せ致します (びむすくりぷと…).

ソースコード

yuzumikan15/ocaml-alab-mode · GitHub

なんかいろいろ入っていますが dev branch の

  1. OCamlMakefile
  2. Makefile
  3. main.ml
  4. my_compile.ml
  5. expander.ml
  6. printtypes.ml
  7. hole.el

があればいいと思います.あと README の Requirements にあるものたち.

全然リファクタリングしてなくてとっ散らかったプログラムなので本当に全方位平謝りです.まさかり投げないでくださいごめんなさい…

型注釈を付ける

今回 alab-mode を実装するために,ユーザが書いているプログラムを OCamlコンパイラに渡して型付きの抽象構文木 (Typedtree) を作ってもらい,それを walk することで hole や環境の型を求めています.なので expression をいきなり hole にしてしまうと全てが多相になってしまいます.何を言っているか分からない人はとりあえずこれだけ覚えてください.

ソースコードには型の annotation を付けて!

(* plus_p: int list -> int -> int list *)
let plus_p lst p = List.map (fun x -> x + p) lst

というコードは

(* plus_p: int list -> int -> int list *)
let plus_p (lst: int list) (p: int): int list = List.map (fun x -> x + p) lst

(* plus_p: int list -> int -> int list *)
let plus_p: int list -> int -> int list = fun lst p -> List.map (fun x -> x + p) lst

という形で型注釈を付けることができます.でないと今デバッグ用のエラーメッセージ出てきてしまいますから!!お願いします!!!

機能

  1. hole の挿入,削除
  2. hole の展開 (expression 指定なし): tuple, record のみ
  3. hole の展開 (expression 指定あり)
  4. 指定した変数での match 式の挿入: list, option, ユーザが定義した型 (record, variant) のみ
  5. if 式の挿入

key bind は README にある通りです.

hole の挿入,削除

agda2-mode では ? が書けるようになっていましたが alab-mode では C-c h で入ります.いきなりハイライトされていますが内部的には (exit(*{}*n)) (n は自然数) となっています.なので alab-mode では exit n が使えないのですね…

gyazo.com

hole の展開 (expression 指定なし)

agda2-mode の hole に何も書かないときの refine と同じことをしています.

(* make_pair: 2つの整数をもらい2つ組を返す *)
(* make_pair: int -> int -> int * int *)
let make_pair: int -> int -> int * int = fun p q -> (p, q)

を書きたいときに最後の expression を C-c h で hole にしてあげて (以下のようになります)

(* make_pair: int -> int -> int * int *)
let make_pair: int -> int -> int * int = fun p q -> {}0

0番の hole のところにカーソルを合わせて C-c r とすると

(* make_pair: int -> int -> int * int *)
let make_pair: int -> int -> int * int = fun p q -> ({}0, {}1)

みたいに勝手に2つ組の形に展開してくれるような機能です.tuple と record に対応しています.

tuple の例.2つ整数をもらってきて2つ組を作ります.

gyazo.com

hole の展開 (expression 指定あり)

hole に expression を書いた状態で refine をします.このとき expression を入れた状態でコンパイラがエラーを出さなかったら refine でき,何かしらのエラーメッセージが出たら refine できないようになっています.

person 型が先に type で定義されていて,make_person ではこの person を作ります.以下では最初に0番目の hole に型の違う変数 age を入れると下のウィンドウにエラーメッセージが出てきて refine できません.

gyazo.com

match 式, if 式の挿入

hole に書いた変数で pattern match をすることができます.現段階では tuple, list, option, ユーザが定義した (同一ファイル内の) 型 (record, variant) に対応しています. また,hole のところで C-c i とすると if 式を挿入できます.

gyazo.com

実際に使ってみる

目標は弊大学の OCaml の授業でまともに使えるようにすることなので,この授業の課題を1つ解いてみましょう.

八百屋木(「文字列(野菜名)と整数(値段)のペア」の木)と 野菜リスト(文字列(野菜名)のリスト)を受け取ったら、 野菜リストの中にある全ての野菜を買えた場合のみ、その代金の合計を返し、 野菜リストの中にひとつでも八百屋に置いていない野菜があった場合は 0 を 返すような 関数 total_price をデザインレシピに従って作れ。

一般的な木の型として以下が与えられているものとします.ちなみに二分探索木とします.

type ('a, 'b) tree_t = Empty
                     | Node of ('a, 'b) tree_t * 'a * 'b * ('a, 'b) tree_t

'a, 'b は型変数なので,この課題の八百屋木は (string, int) tree_t となります.

youtu.be

issue

github の issue の通りですが目下の課題は

  1. next goal, previous goal の key bind による移動
  2. 任意の型 (を持つ変数) について pattern match (現在決め打ちで出力している list, option などの OCaml が定義している型は,本当はそこまで探索できるようにしたい)
  3. ocaml-alab-mode を minor mode にする

です.あとなんか無駄に改行が入るのと,pattern match したときのコンストラクタの引数名どうにかしたいですね……

まともに使えるようになったら来年度の OCaml の授業はこれと,型デバッガと,あともう一つ秘密兵器を使えるようになります (論文とかの関係で言っていいのか分からないので伏せておきます…).みんな頑張ってください.

Nearby Messages by Swift

この記事は 学生エンジニア Advent Calendar 2015 17日目の記事です.

こんにちは.

一昨日引越しして荷解きと収納が嫌になったので普段は書かないブログを積極的に書いていこうと思います.

今日は Google の Nearby Messages API についてです.

developers.google.com

この API は今年の Google Play Services 7.8 リリースの際に追加された Nearby API の中の1つで, 音を使って他の端末と情報共有をするための API です.iOS 版はまだ β版らしいですが iOS 7.0 以降で使えるみたいです.

Nearby には今回紹介する Nearby Messages APINearby Connections API があります.Messages は文字通り複数の端末とメッセージを共有するために,Connections はこちらも文字通り複数の端末に接続するために用いられます.Nearby API を使うときは端末の Wi-Fi, Bluetooth 共にオンである必要があります.

既に試してみたというブログや,この API を使った Android アプリの紹介があったりするのでそちらもどうぞ.

Android - Nearby Messages APIでチャットみたいなのを作ってみる - Qiita

Nearby Message API を試してみた - 質量

Nearby API であなたを取り巻く世界とつながる - Google Developer Japan Blog

さて,これらは全て Android の記事ですので,今回は iOS で試してみたいと思います.というか,以前ハッカソンで初めて使ったのでそのときのことを書きます.

(雑な記事です.紹介したブログのような親切さは欠片ほどもありませんので予めご了承ください)

API の有効化

こちらはほぼ Android と同じ手順です.Android 版は先ほど紹介した記事にもありますし,iOS 版は英語だったら公式に載っているので読んでください.

developers.google.com

ざっくり日本語で説明すると,

  1. 最新の Xcode をインストール (6.3 以降なら OK っぽいです)
  2. CocoaPods をインストール
  3. Nearby API を CocoaPods で入れる
  4. Google アカウント作成
  5. API Key の取得
  6. プログラム中で message manager object を作成

となります.

1, 2 の Xcode や CocoaPods, 3 の pod install あたりは iOS 開発者なら問題ないと思います.また,4 の Google アカウント作成もだいたいの方は既に (いくつか …???) 持っていらっしゃると思います.(ちなみに今流行りの Carthage にも入っているかは分からないです.知見共有お願い致します)

5 以降,Google Developers Console を使って API を有効化します. 公式に書いてあるそのままですが,

  • API Key の取得
    1. プロジェクト作成 ... (a)
    2. Nearby API の有効化
    3. Client key の作成

という手順を踏みます.

各項目の細かな手順は公式に書いてありますし,割と親切な GUI なので迷うことはないと思います.Google Developers Console を初めて使った私ですら大丈夫でしたので…

iOSAndroid

ハッカソンで使ったときは私が iOS, もう一人のエンジニアが Android 担当で Nearby Messages を使って情報をやり取りしていました. 公式の手順5の下に ★ Note とあるように,iOS, Android 両方使いたいときは,(a) で同じプロジェクトを使ってください.当時は Android 側で作ったプロジェクトに iOS の project name と bundle identifier を追加して API Key をもらっていたような気がします.

Bridging-Header

公式通り Objective-C で書いている方はここは飛ばしてください.Swift で書いている方は,Nearby APIObjective-C なので Bridging-Header を作ります (Bridging-Header を作ったことがある方もここは飛ばしてくださって大丈夫です).

YourProjectName-Briding-Header.h に

#ifndef YourProjectName_Bridging_Header_h
#define YourProjectName_Bridging_Header_h
#import <GNSMessages.h>
#endif

を書いて,

  • TARGETS
    • Swift Compiler - Code Generation

を設定します.

Create Message Manager Object

手順 6 でようやっとプログラムに何か書きます.Message Manager Object (GNSMessageManager) の初期化です.Message Manager Object は Publication, Subscription を管理するためのクラスです.たぶんどこで初期化してもよいのですが,ハッカソン時はアプリ起動時から pub / sub を行いたかったので AppDelegete.swift 内に書きました (本当は Nearby Messages 用のクラスを別に作って AppDelegate 内でインスタンス作るとかの方がよいのでしょうけど,ハッカソンなので設計などは許してください).

具体的には

let nearbyAPIKey = "Your Nearby API Key"
var messageManager: GNSMessageManager?

func initMessageManager () {
    messageManager = GNSMessageManager(APIKey: nearbyAPIKey)
}

でいいと思います.思います,というのは,私のソースコードにはそう書いてないからです.公式もそうなっていますが,今後,マイク使用の許可,Wi-FiBluetooth がオンになっているかなどのトラッキングをするのでここはもうちょっと膨らみます (なので最初から initMessageManager を作っておきました).

アプリの初回起動でこんなダイアログがでてきて,Nearby API を使うのに必要なマイク,Wi-FiBluetooth の使用許可を求めてきます.

gyazo.com

ちなみに messageManager は Optional にしていますが,今流行りの - [要出典] lazy var でもいけるんじゃないでしょうか.var + ! ?? 知らない子ですね.

Pub / Sub

いよいよメッセージの送信,受信をしてみましょう.

ユーザ設定と Permission のトラッキング

その前に,先ほど書いたユーザへの Nearby API 使用のための様々な許可 etc. のトラッキングを設定していきます.

ユーザ設定のトラッキング

公式 だと 'Tracking user settings that affect Nearby' という部分をやります.先ほどの initMessageManager では マイク,Wi-FiBluetooth 使用の Permission 確認ダイアログが出てきただけでした.ここでユーザがこれらの使用を許可しなかったり,Bluetooth がオフになっているときにアラート (アラートじゃなくてもいいのですけど) を表示してユーザに Nearby API が使えないことを知らせることができます.次のサンプルではとりあえず print しているだけです.適宜,何かしらの UI を作ってください.

func initMessageManeger () {
    messageManager = GNSMessageManager(APIKey: nearbyAPIKey) {
            (params: GNSMessageManagerParams!) -> Void in

            // This is called when microphone permission is enabled or disabled by the user.
            params.microphonePermissionErrorHandler = { hasError in
                if (hasError) {
                    print("Nearby works better if microphone use is allowed")
                }
            }

            // This is called when Bluetooth permission is enabled or disabled by the user.
            params.bluetoothPermissionErrorHandler = { hasError in
                if (hasError) {
                    print("Nearby works better if Bluetooth use is allowed")
                }
            }

            // This is called when Bluetooth is powered on or off by the user.
            params.bluetoothPowerErrorHandler = { hasError in
                if (hasError) {
                    print("Nearby works better if Bluetooth is turned on")
                }
            }
        }
}

ちなみにこれは

GNSMessageManager(APIKey: nearbyAPIKey,
                    paramsBlock: {
                        (params: GNSMessageManagerParams!) -> Void in
                        ...
                        ...
                        })

の Trailing Closure です.でもここまで長いと Handler を別に定義してあげるのがよさそうですね…あと抽象化したい…

Permission のトラッキング

公式 の 'Tracking the Nearby permission state' のところです.ユーザが Nearby API の使用を許可した / しなかったときに何かしらのアクションを起こすことができます.Permission Tracking には GNSPermission というクラスを使います.GNSPermission.isGranted() で 許可したかどうかを取ってくることができ,GNSPermission.setGranted (granted: Bool) で permission を set します.

次のサンプルは,画面の左上にボタンを作り (i.e. ツールバーの left button),ボタンを押すことで Nearby を許可したりしなかったりを切り替えるものです.

var nearbyPermission: GNSPermission?
// messageViewController: ツールバーを設置する画面 (ViewContoller)

func setupNearbyPermission () {
        let changedHandler: Bool -> Void = {
            [unowned self] granted in
                let answer = String(format: "%@ Nearby", granted ? "Deny" : "Allow")
                self.messageViewController.leftBarButton =
                    UIBarButtonItem(title: answer,
                                    style: UIBarButtonItemStyle.Plain,
                                    target: self,
                                    action: "toggleNearbyPermission")
        }
        
        nearbyPermission = GNSPermission(changedHandler: changedHandler)
    }
    
// Toggles the permission state of Nearby.
func toggleNearbyPermission() {
    GNSPermission.setGranted(!GNSPermission.isGranted())
    }

公式にも注意書きがありますが,ユーザの意図に反して permission を set しないでくださいね.

Publication

長かった…ここまで本当に長かったです…ブログ書くのって本当に体力使いますよね……

公式だと Publishing a message のところです.

GNSMessageManagerインスタンスmessageManager に送りたいメッセージを入れて GNSPublication クラスのインスタンス (以下では publication) に渡してあげます. publicationnil ではない間ずっとメッセージは送信し続けられます.逆に,publicationnil にすると送信は止まります.以下はメッセージとして JSON を送るようにしています (公式ではメールアドレスですね).ちなみに JSON を扱うのに SwiftyJSON を使っています.

var publication: GNSPublication?

func startPublication () {
        if let mgr = self.messageManager {
            let json = JSON(someDictionary)
            do {
                // JSON.rawData() が exception を出すようになっているので try します
                let message = GNSMessage(content: try json.rawData())
                
                // ここで publication にメッセージが入った時点で publish が開始される
                publication = mgr.publicationWithMessage(message)
            }
            catch {
                // 本来はちゃんとなんらかの対処をしてあげてください
                print("json.rawData() throwed an exception")
            }
        }
        else {
            // 本来は (ry
            print("messageManager is nil")
        }
    }
    
    func stopPublication () {
        publication = nil
    }

Subscription

次はメッセージの受信です.

こちらも Publication のときと同様,messageManager でハンドラなどを設定して GNSSubscription クラスのインスタンス (以下では subscription) に渡してあげます.subscriptionnil ではない間ずっと受信できるよう待機しています.

受信する際,subscript にはなんらかのメッセージを受信した際に呼ばれるハンドラと,メッセージを失ったときに呼ばれるハンドラの2つの GNSMessageFoundHandler が渡されます.

func startSubscript () {
    let messageFoundHandler = {
        [unowned self] (message: GNSMessage!) in
        let messageStr = String(data: message.content, encoding: NSUTF8StringEncoding)
        if let data = messageStr?.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
            let content = JSON(data: data)
            ...
            ...
        }
        else {
            // 本来は (ry
            print("data is nil")
        }
    }
        
    let messageLostHandler = {
        [unowned self](message: GNSMessage!) in
        if let content = String(data: message.content, encoding: NSUTF8StringEncoding) {
            // 本来は (ry
            print("Lost message: \(content)")
        }
        else {
            // 本来は (ry
            print("cannot convert message.content to String")
        }
    }
        
    subscription = messageManager.subscriptionWithMessageFoundHandler(messageFoundHandler,
        messageLostHandler: messageLostHandler)
}

func stopSubscription () {
    subscription = nil
}

使ってみて

ハッカソンのときは私ももう一人も Nearby を使うのは初めてでしたが割とすんなり使えて,しかもちょっと大きめの JSON をやり取りすることができたので,今後なにかをサクッと共有したいときには使っていけるなぁと思いました.ちょっと新しい技術使ってる感があって (あとこなしちゃん -後述 がいたのもあって) ハッカソンはとても楽しかったです.

ちょっとよく分かってないのですが,GNSPublicationGNSSubscriptionnil になると送受信が止まるというのはどうなんでしょうね? 公式の Android 版 をみると

@Override
protected void onStop() {
    if (mGoogleApiClient.isConnected()) {
        // Clean up when the user leaves the activity.
        Nearby.Messages.unpublish(mGoogleApiClient, mMessage)
                .setResultCallback(new ErrorCheckingCallback("unpublish()"));
        Nearby.Messages.unsubscribe(mGoogleApiClient, mMessageListener)
                .setResultCallback(new ErrorCheckingCallback("unsubscribe()"));
    }
    mGoogleApiClient.disconnect();
    super.onStop();
}

こんな感じで unpublish なるものが存在していて,こちらのほうがよいのではないかなと思うのです (というか Android 版めっちゃ親切にいろいろ書いてありますね…!!).

(GNSPublicationGNSSubscriptionインスタンスGNSMessageManager から設定を渡されるとき以外出てくることはないので,そのうち意図的にではなく implicit に nil になってしまうこととか…ないですかね… GC 的な…ない?)

ソースコード

あるけどまだどこにも載せてないですすみません. ハッカソンのときは受信したメッセージを元に Konashi にコマンドを送ったりしていたので今回のお話にはちょっと余計なコードが入っているのと,ちょっと汚かったのでリファクタリングしてたのですがよく考えたら端末を1台しか持っていなくて動作確認ができない,というのが理由です.本当にすみません.(Android 用のも書いて動作確認してみるかなぁ…)

次回は kmd_09 さんです.

セルゲイ・ラフマニノフ 前奏曲嬰ハ短調 Op.3-2 (鐘)

この記事はクラシック曲 Advent Calendar 2015 8日目の記事です.

5歳の頃から高校までピアノを弾いていました.クラシックはショパンノクターンから入り,今回紹介するラフマニノフの鐘が,おそらく最後の発表会で弾いたクラシック曲です.浅田真央選手が使っていたので知っている方も多いでしょう.

曲を聴く前に,皆さんは「鐘の音」と聞くとどんな音を思い浮かべますか?

私が譜面も作者も知らないときにタイトルだけからイメージしたのは,お寺の鐘の音でした…除夜の鐘みたいな……日本人ですからね…

先生から譜をもらってとりあえず弾いてみるとなんとも暗い.遅い.そうかと思うと突然慌ただしくなる.全然イメージがわかなくて CD ショップ行って様々なピアニストの鐘を試聴しました.でも本当になんでこの曲が鐘なのか,どこが鐘なのか分からなかったです.

慌ただしいところ以外はテンポも遅く,複雑な音符もないので,練習するモチベーションがあんまりなかったのですが,そんな中でもいろいろ試聴はしていて,あるときついに,ワイセンベルクの鐘を聴いたとき,きました,鐘の音.

www.youtube.com

ロシアの教会だ!ロシアの,モスクワあたりの古い教会の鐘の音だ!モスクワはおろかロシアなんて行ったことないけどこれはロシアだ!って思いました (よく考えたらラフマニノフはロシア人だしこの曲はモスクワでお披露目されたのでそりゃそうだろうという感じなのですが). ロシアだしきっと冬はめっちゃ寒い.寒くてとても暗い,空気も重いんだろう.外に出る人なんて全然いない中で教会の鐘が重厚な音を響かせている,そんなイメージが完全にできあがりました.

2'39'' からのラストは低音部の重厚な和音と高音部のかっちりとした音がどちらも同じくらいの音の大きさ (譜は忘れましたが fff くらい?) で交互に出てきて,いかにもゆっくりと左右に揺れて音を出す鐘って感じがするのはよい.そこはどのピアニストもそうでした. ワイセンベルクが他と違ったのは,最初の方や,一番最後の小さな和音でさえも金属音のようなカチッとした音で弾いてるところです.最初から最後までずっと鐘の音でした.除夜の鐘なんて想像して本当に申し訳ない限りです.

そんなこんなでこの曲は私にとって思い出深い曲の1つとなりました.大学に入ってフィンランドへ留学したついでにヨーロッパをちらほらとまわってきたのですが,どの町へ行ってもとにかくまず教会へ足を運びました.今でも冬になって風が身にしみるようになると必ず聴きたくなります.

でも実はまだロシアに入ったことがないのです…いつか真冬のモスクワに行きたいなーとは思ってるのですが…

最後に,ラフマニノフ自身が演奏しているものを.

www.youtube.com

明日は Ptu_ さんです.

Noël de Quatre-Mains - Les Frères

この記事は 好きなクリスマスソング Advent Calendar 2015 の4日目の記事です.

毎年12月に入ると必ず聴くアルバムがあります. タイトルにもなっている,Les Frères の Noël de Quatre-Mains です.

amzn.to

Les Frères は横須賀出身のジャズピアニストで,名前の通り実の兄弟です.2人で1つのピアノを使って連弾をします. 'Noël de Quatre-Mains' は 'Christmas of Four-Hands' のフランス語で,'quatre-mains' (4つの手) というのは Les Frères の代名詞にもなっています.

ジャズの中でも Boogie-Woogie 曲やアレンジが多いので,彼らの音楽はとにかく踊れます. (ブギウギはブルースをアップテンポにして踊れるようにしたものだ,とどこかで聞いたような…) このアルバムは,皆さんがこの時期よく聞くであろう様々なクリスマス・ソングを boogie-woogie アレンジしているものです.

さて,このアルバムは素晴らしく幸せになれるのでぜひとも全曲聴いていただきたいのですが, 初めての方も多いかと思うのでいくつかピックアップしてみました.

Jingle Bells

とりあえずこのアルバム内ではおそらく最もキャッチーなアレンジ曲です.

youtu.be

(本人たちが弾いているものがなかったのでこちらで…)

こんなに挑発的で悪そうなジングルベルなんて聴いたことないです. 1'54 からの最高音の倦怠感なんかもう本当に素晴らしくて一気に脱力してしまいます.

Noël de Quatre-Mains

アルバムのタイトルにもなっている曲です.

youtu.be

とにかくこの1曲だけでクリスマス・ソングを堪能できてひたすら幸せになれる,そんなメドレー曲です. クリスマスの昼間からホットワイン飲みながら聴いていたい…

Rudolph The Red-Nosed Reindeer

赤鼻のトナカイ.

youtu.be

聴いてる途中で「あ,これ赤鼻のトナカイか!」と毎回気付くくらい boogie になってしまっています.

Boogie Christmas

最後の〆!

youtu.be

最初に紹介した Noël de Quatre-Mains と同様,こちらもクリスマス・ソングメドレーですが,前者よりももっと boogie になっています. 本当に楽しい1曲です.

さいごに

今回紹介したのはアルバムの中でも氷山の一角で,本当はもっと聴いてほしい曲がたくさんあるのです… それから,本人たちが演奏している音源が少ない (今回のアルバムに関しては0でした…) のも悲しい…

悔しいので彼らの曲で私が一番好きな曲を最後に貼っておきます.

G Sign

クリスマス全然関係なくてごめんなさい!!!!!でもかっこいいから最後まで聴いてください!!!!!!!

youtu.be

好きなクリスマスソング Advent Calendar2015,次回は taketin さんです.

CPS というプログラミングスタイルの導入の話

今日 (もう昨日だけど) 大学の授業で CPS というプログラミングスタイルの話を聞きました.これを読んでいる人はきっと CPS が 'Continuation-Passing Style (継続渡し形式)' のことで,それがどのようなスタイルのプログラミング手法なのかも分かっていて,さらに JS を CPS で書いたり継続を限定してみたりしている人ばかりだと思うので,そういう「どう使うか」みたいな話はしません.できません.ケイゾクムズカシイ.

今回は授業で「プログラミング中のどこで CPS という概念に辿り着くか」という 'CPS の導入' の部分を聞いて,それが今まで聞いてきた 'CPS の導入' の話の中で一番しっくりきたので備忘録も兼ねてご紹介します.継続プロの方々には当たり前の話かもしれません.それからできるだけ分かりやすくするために細かい動きなどはあんまり言及していません.Overview だと思っていただければ幸いです.あと OCaml で書きます.

eval 関数

授業で何をしていたかというと,電卓を作っていた,つまり,ユーザが書いた式を評価するインタプリタを作っていたわけです.その '評価する' 関数が eval です.

例えばユーザが

3 + 5

という式をインタプリタにかけて計算しようとしたとき,インタプリタの中でこの式は + が関数,3 が第一引数,5 が第二引数である

+ 3 5

という関数適用の式に変換されてから,各部分式 (+, 3, 5) をそれぞれ eval して,関数適用が行われ,適用の結果がまた eval され,ユーザが求める計算結果 (8) が返ってきます. (ボトムアップのように書きましたが,本当はここは全体を eval するために各部分式を eval していく再帰構造になっています)

評価順序

さてここで先ほどの + 3 5 はどの部分式から評価されるでしょうか?はい,It's up to you です.あなたがもし関数部分 (+) から評価するインタプリタを書きたいと思ってそう開発してあげれば,+ 3 5 は関数である + から評価されます.では引数部分はどうでしょう?インタプリタ内部では各引数はリストに入っています.今回の場合は [3; 5] というぐあいです.このリストの先頭から評価するか,最後尾から評価するか,それもあなたの意思次第です.

で,実は OCaml はこのリストの最後尾から評価していく戦略を取っています.つまり,

+ 3 5

の引数部分は 5 から評価されます.もっと言うと,

3 + 5

は右側の引数から評価されます.よく分からないのですが,OCaml の場合は最後尾から評価した方が後々効率がよいのだそうです.

'式のどこから評価するか' は言語の開発者の意思次第です.普通の計算なら答えが合っていればいいと思う人もいるかもしれません.でも次の場合はどうでしょう?

ここで OCaml のお勉強です.print_stringstring 型の値をもらってきたらそれを出力して unit 型の値を返します.unit() という値しか持たない型で,とりあえず void だと思ってくれればよいです.print_newline はその () をもらってきたら改行を出力して () を返します.

また,;逐次実行です.; はそれ以前に書いた式が返した値を破棄して次の式の評価へと進みます.上の例の * の左側の引数の場合,まず print_string "left" が返した値 () を捨てて次の式 print_newline (); の評価へと進みます.ところがここでも ;print_newline () が返した () を捨てて次の式を評価しにいきます.最後に 5 - 3 の評価は最終的に 2 になるので,* の左側の値は最終的には 2 となり返ってきます.

実際に OCaml でこの式を評価してみると

f:id:yuzumikan15:20150424052437p:plain

OCaml は右から評価するので,ちゃんと right から出力されています.

同じような式を他の言語で書いたら,言語によって rightleft の出力結果が違ってきます.これは困りものです.同じ式なのに,ユーザが見る画面に書いてあることが違ってきてしまうわけです.

評価順序を統一する

どんな言語で書いても同じ結果になるように,評価戦略によらず評価順序を統一したい.とりあえず OCaml で書くときは let 文を書いて上から順番に評価されるようにします.

let は変数束縛です.ここで束縛した変数は in 以下で使えるようになります.例の場合,n1

(print_string "left"; 
 print_newline ();
 5 - 3)

を評価した結果が入り,in 以下で使えるようになります.次に n2

(print_string "right";
 print_newline();
 5 + 3)

を評価した結果が入り,in 以下で使えるようになります.最後に,

n1 * n2

を評価した結果がこの例全体の最終的な結果として返ってきます.

ここで重要なのは,let で変数が束縛されるときに = の右側の式は評価される ということです.n1

(print_string "left";
 print_newline ();
 5 - 3)

を入れるとき,この式は評価されるので標準出力が行われます.n2 についても同様です.例を実行してみると

f:id:yuzumikan15:20150424054751p:plain

今度は left から出力されました.このように,OCaml だったら let 文を書くことで評価順序を制御できます.

では Haskell はどうでしょう?Haskell の評価戦略は '遅延評価' と呼ばれる戦略です.これは,書いてある通りに評価を行なうのではなく,その式がどこかで本当に使われるときに初めて式の評価を行います.上の例 (と同じことが Haskell で書けたとしましょう) だと,let n1 = ... の時点で ... の部分はまだ評価されずそのままの形で n1 に入ります.n2 についても同じです.そのままの形です.最後に n1 * n2 が出てきたときにやっと,Haskell は「あ,n1 必要じゃん」と思って n1 に入っている式

(print_string "left";
 print_newline ();
 5 - 3)

を評価します (たしか Haskell は素直に左側から評価だった気が…間違ってたらごめんなさい).この戦略だと,たとえ let を使っても,実際に変数が使われるタイミングによってやっぱり結果が違ってきてしまいます.今回の例ではきっと同じでしょうが,例えば最後を n2 * n1 と引数の順序を逆にするときっと結果が変わってきます.

カプセル化

どんな評価戦略でも評価順序を制御できるような書き方はないものでしょうか? (反語)

元々評価したい式は

でした.これを必ず左側から評価したいとしましょう.まず,なぜ評価順序がいろいろ変わってしまうかを考えます.それは,左側も右側もどちらも評価できる式になっているから です.どちらもまだ評価できる式になっているのがダメ.ならば,どちらかを評価できない式に変えてあげましょう.今回は左側から評価したいので右側を評価できない式にします.

ここでもうちょっと OCaml のお勉強です.OCaml というかみんなだいすき TAPL のお勉強です.

OCaml で変数をスコープ内に入れる方法は2種類あります.1つはさっき使った let,もう1つは fun 文です.fun 文は要するにラムダ式 (無名関数) のことです.fun x -> x + 1 は λx.x+1 のことで,Haskell で書くと \x -> x + 1 です.これで x をスコープ内に入れて -> 以下で使うことができます.何が違うって束縛した変数が多相型か単相型かの違いですがここでは考えません.

今回評価順序を統一するにあたって fun を使うと嬉しいことがあります.funこれ以上評価できない式 (= 値) なのです.詳しくは TAPL を読んで下さい.ここではとりあえず fun の中に書いてあることには触れられない,という説明にします.この fun を使って,評価したくない右側の式をまるっと包んでカプセル化してあげます.が,慣れていないのでまずは評価したい左側だけを let で束縛してみます.

次に let ではなく funn1 を束縛してみます.このとき n1 を使いたいのは左側の式を評価したあとなので左側の式のあとに fun n1 ->... を書きます (fun n1 -> 左側の式 としてしまうと,fun の中身には触れないので左側の式も評価されなくなってしまいます).

(capsule1.ml はコピペしても実行できないので注意してください)

これで,fun の中にある右側の式には触れられなくなって,fun の前にある左側の式から評価せざるを得なくなりました.ついでに右側の式も fun を使って束縛してあげましょう.まずは先ほどと同じように letn2 を束縛してあげます.

次に n2fun で束縛してそれ以降に行なう式 n1 * n2カプセル化します.カプセルにする fun n2 -> は右側の式を評価したあとだということに注意してください.

この式をどう読むかというと,

(print_string "left";
 print_newline ();
 5 - 3)

を評価し,その結果返ってきた値を n1 に入れて,

(print_string "right";
 print_newline ();
 5 + 3)

を評価し,その結果返ってきた値を n2 に入れて,

n1 * n2

を評価する.と読みます.

c は capsule の c

でもこれはまだ実行可能なプログラムではないですね.完全なプログラムにしてみましょう. capsule3.ml において何がおかしいかというと,左側の式

(print_string "left";
 print_newline ();
 5 - 3)

の値は 2,つまりただの数字なのに,そこに fun n1 -> ... という右側の式を引数のように渡してしまっていることです.というか,本当は 2n1 に入ってほしいので,

(fun n1 -> (右側の式)) 2

という関数適用が正しい形のはずです.では,(左側の式) (fun x -> 右側の式) を受け取ったら (fun x -> 右側の式) (左側の式) という関数適用を行なう関数を定義してしまいましょう.

終了です.c は capsule の c です.決して continu(ry

皆さん OCaml くらい書けると思うので説明はいらないかと思いますが,

let capsule x c = ...

let capsule = fun x -> fun c -> ...

と同じです.要するに関数 capsulexc という2つの引数をもらってきますよ,と読みます.

ではこの capsule を使って実際に capsule3.ml を動かすプログラムを書いてみます.

最初の capsule の第一引数 x

(print_string "left"
 print_newline ();
 5 - 3)

が入って,第二引数 c

(fun n1 -> capsule (print_string "right";
                               print_newline ();
                               5 + 3)
                               (fun n2 -> n1 * n2)
 )

が入っていて,capsule x c = c x なので

(fun n1 -> capsule (print_string "right";
                               print_newline ();
                               5 + 3)
                               (fun n2 -> n1 * n2)
 ) (print_string "left"
    print_newline ();
    5 - 3)

が実行されることになります.fun n1 -> ... の中には入れないので置いておいて,引数部分を評価しに行きます.ここで left と改行が出力されて 5 - 3 を評価し,2 が返ってきます.引数がやっと値になったので今度は関数適用が行われ,n12 が入った状態で fun n1 -> 以降の式を実行しにいきます.

次の capsule についても同様の動きです.n2 には 8 が入り,n1 * n2 が実行されて最終結果は 16 になります.

f:id:yuzumikan15:20150424082633p:plain

c は continuation の c

さて,長かったですがやっとどんな評価戦略であっても評価順序が同じになるプログラムを書くことができました.何をしたかというと,まだ評価してほしくない部分を funカプセル化して中を触れないようにし,先に評価してほしい式から返ってきた値を fun で束縛している変数に入れて -> の先の式を評価しに進んでいくのでした.

先に評価してほしい式に対して,あとで評価してほしい式のことを 'continuation, 継続' と呼びます.あとで評価してほしい式は先に評価してほしい式の '継続' になっている,という使い方をします.

そして,あとで評価してほしい式を fun でくるんで継続にして持ち歩くプログラミングスタイルのことを 'Continuation-Passing Style, 継続渡し形式' と呼びます.一般的に,さっき引数で書いた ck というアルファベットで書きます.これは 'continuation' の 'c' がギリシャ語だかラテン語だかでは k になるからだとかなんとか…?

最後に,授業で紹介された Continuation-Passing Style (CPS) の約束事を書いて終わりにします.

  • 常に (serious な) 関数適用は末尾呼び出しになっている
  • serious な関数には 'その後の仕事' を表す引数 k が1つ増える (= 継続)
  • 値を返すときには k に渡す

ここで serious な関数というのは '評価順序を気にしたい' 関数 (= CPS で書きたい関数)という意味だそうです.反意語は 'trivial' な関数.例えば + という関数が serious か trivial かはプログラマの意思に依存します.+ の評価順序が左からだろうが右からだろうが関係ないと思えば + は 'trivial' な関数になり,きちんと厳密に評価順序を制御したいと思えば 'serious' な関数になります.

CPS で書くことの特徴として,システムの stack に今後の仕事を積んでいく代わりに,自前で関数の形で作って持ち歩いているということがあります.これによって,例えば評価中のどこかでエラーが発生したときにそのエラーを今後の仕事に propagate していくのではなく,エラーになったら今後の仕事は全部捨ててそのまますぐにエラーを返す,ということができます.無駄を省けます.

今後の授業 (この分野に詳しい人向け)

この授業,情報科の大学院の授業で,数理論理学の人たちと私が所属している研究室 (型理論とか証明とか) の人たちが主な参加者です.もちろん CPS という単語は知っていますし,今までのゼミでも書いたことがある人たちが多いです.ではどうして CPS なんかやったのかというと,授業では big-step semantics と small-step semantics と abstract machine の話をするらしく,今のところの目標として,big-step semantics と small-step semantics を行き来する (変換する) プログラムを書くからのようです.どういうことかというと,big-step と small-step を行き来する道具として,CPS の big-step + α を用いるのだそうです.もうちょっと言うと,big-step semantics の継続が small-step semantics になっている のだそうです.ふむふむ.なんかそんな感じする.

概念だけ聞いてるととても面白いんですけどね,継続…

OCamler がリクルートのインターンで Python を書いてきました

2月1日〜23日まで,株式会社リクルートホールディングス (以下,リクルートさん)の冬インターンに参加してきました.

http://recruit-jinji.jp/winter-internship2015

実は去年の夏に,リクルートさんとクックパッドさんが共同開催した Joint!! という5日間のインターンに参加していて,予想以上に得られるものが多かった(お金はもらってません)ので今回もぜひ行きたいと応募したインターンでした.15万も美味しかったですし.

ちなみにこの時の私がとても楽しそうだったという理由で研究室の同期は今回応募,参加したらしいです.まだ,ここにない,出会い.素晴らしいです.

 

選考

Swift アイドルなので普段は iOS 開発をしているのですが,Web の知識をもっとちゃんと付けたいという理由でフロントサイドコースを選びました.

選考は,ES を書いて,AtCoder のプロコンテストがあって,1day job という流れです.

1day job,他のコースは6時間くらいで何かしらがっつり実装する,という内容だったようですが,フロントサイドは15分間の(Web 開発に関する)自慢大会でした.

とりあえず今までやったことを絞り出して,自己紹介でOCamlSwift と某Y社ハッカソン優勝という単語をしっかり入れてプレゼンしてきました.

時間が余ったので 懇親会で選考に来てる学生たちとまだ,ここにない,出会い.をしてました.楽しかったです.

 

インターン

Joint!! のときは1チーム4人のうち2人がプランナー,2人がエンジニアで,かなり企画重視のインターンでしたが,今回は1チーム3〜4人の全員がエンジニアという構成でした.企画も最初の1週間弱であとはひたすら開発だったのでエンジニアからしたらとてもわくわくするインターンだったのではないかと思います.「この問題を解決するためにどんな技術を使えるか」という,技術的解決のための挑戦をできたのはとても嬉しかったです.

さらに,今回は AWS などの普段は使えないサービスをがっつり使えたのもよい経験でした.

ちなみに,私は夏にがっつりやった Lean Canvas による企画なんかもとても重要だと思っていて,その経験があるからこそ現在趣味開発をできているので,企画をおろそかにしてよいとは全く思っていません.ただ,エンジニアだけのインターンではやっぱり技術や開発重視になっているととても嬉しいのは確かです(正直 Lean ってとても難しいですし,エンジニアとしては開発に入れないフェーズは大変つらいので…).

 

'Indeed のうえ'

私たちのチーム(以下,MIT)は「表面上」フロントサイド2人,バックエンド1人の3人構成で,メンターの方が1人付いて下さいました.

5つのテーマから1つを選び,そのテーマに則したサービスを1つ開発する,という課題だったので,MIT は「将来のキャリアに役立つバイト探し」をテーマとして選択,「将来エンジニアを目指す学生のためのエンジニア求人サイト」を作りました.'Indeed のうえ'というサービス名です.よろしくお願い致します.

企画の段階でアンケートを実施して(100人くらい回答が集まりました.ありがとうございました.)その中で多かった(お茶濁し)「探し方が分からなくてエンジニアバイトに応募できない」という問題を解決しようという方向になりました.

で,技術として何を使えるか,ですが,

ネット上にあるあらゆるエンジニア向けの求人情報をスクレイピングして,そのバイトがどんな技術を用いたものなのかをタグ付けしていく,ということをしていました.

工夫したのはタグ付けの部分で,フレームワークから使っている言語を紐付けて親タグとして登録してあげたり,使っている言語によってその仕事が Web 系なのかモバイルアプリ系なのかなどの分類をしていました.

これを見ている方々には自然言語よりも形式的な書式の方が分かりやすいと思うので簡単な BNF もどきでも貼っておきますね.

 

タグの構成:

Genre = Web | Mobile | [Design] | Other

Web = [(Language, [Framework])]

Mobile = iOS | Android

Design = 'Illustrator' | 'Photoshop' |'Dreamweaver'

Other = IDE | Others

Language = (55個のプログラミング言語おのおの)

Framework = (Language で代表的なフレームワーク)

iOS = [Device]

Android = [Device]

Device = 'iPhone' | 'iPad' | 'Apple Watch' | 'Smartphone' | 'Wear' | 'Tablet'

IDE = [(6つの代表的な IDE)]

Others = [(その他,使用する OS や DB など)]

※ あくまで「もどき」なので細かいところ突っ込まないで下さいお願いします恐いです.

 

それから,なんやかんや自然言語を解析してそのバイトが「初心者向け」なのか「上級者向け」なのかというレベルに分けていましたが,ここは担当の社長が書いてくれるでしょう.

ちなみに 'Indeed のうえ'という名前,ネタでもありますが,私のプログラムだと 'C', 'C++', 'C#', 'Objective-C' を全て別の単語として認識可能なので,そういう意味で 'うえ' です.もちろん 'Java' と 'JAVA' は同じ単語,'JavaScript' は別の単語です.まぁでも最初は完全にネタでした.

使っていた技術とか

Python のコードは,OCaml に書きなおしたものと一緒に後日載せようと思っているので今回はざっくりとした自然言語で.

Cloudsearch2

DB の代わりに AWS の Cloudsearch2 というものを使っていました.細かいことは社長が書いてくれると思うのですが,クエリを投げたときにうまいこと自然言語処理っぽいことをやってくれるのでシソーラスとかその辺のアレがアレで採用しました.この辺のことはメンターさんからの助言が元になっているのでとても感謝しています.ご飯も美味しかったです.ごちそうさまでした.

Python

技術というか私が担当していたタグ付けのところで使用した言語です.Python にした理由は正直なくて,私が単に言語屋として興味があったから,ただそれだけです.

タグの階層化のところで enum をうまく使って代数的データ型を作りたかったのですが,Twitter や LINE なんかで無理(意訳)と言われ続けて挫折,タグを全て String で保持するという暴挙に出ました.

が,

ここで Python にこだわらずロジック部分は素直に OCaml で書いてしまえばよかったと本当に本当に後悔しています.今回優勝できなかったこと以外に後悔があるとすればここですね.

boto

Python から Cloudsearch / Cloudsearch2 を使うためのインタフェース.チュートリアルがけっこうひどくてちょっと時間かけすぎてしまったのが残念.

その他

html5 とか sass とか compass とかを使いました.この辺のいろいろは全部社長が教えてくれました.本当にありがとうございました.お仕事も頑張ります.

ちなみに普段は Aquamacs という EmacsMac 版を使っているのですが今回は Sublime と仲良くなりました.仲良くなりすぎて Aquamacs のキーバインドを忘れかけました.危ない危ない.

(その他と書いているけど私はフロントサイドのはずなのでこれらはその他に入れてはいけない気がします…)

写真とか

f:id:yuzumikan15:20150301124222j:plain

肉ルートさん♡

f:id:yuzumikan15:20150301125145j:plain

TECH LAB PAAK というリクルートさんのコワーキングスペースの7Fには4Kディスプレイがありました

f:id:yuzumikan15:20150301124020j:plain

スタバで買ったコーヒー豆を PAAK でひくなど

f:id:yuzumikan15:20150215205207j:plain        f:id:yuzumikan15:20150221182130j:plain

PAAK さんの素晴らしさ

f:id:yuzumikan15:20150301125654j:plain

フォアグラといちごぱふぇを同じ日に食べて本当に幸せでした

 

最後に

今回は3週間もあったのでいろんなインターン生と仲良くなれました.しかも今回来ていたインターン生はみんな強い人たちで,ギークな話がたくさん聞けて,話せて,本当に天国でした.

個人的にはこういう強いエンジニアたちに OCaml の布教,すごいはすける本と TAPL の貸出,型環境という単語の認知ができたことを大変光栄に思っています.

まだ,ここにない,出会い.をたくさん提供して下さったリクルートさんに圧倒的感謝.ここでの出会いをここで終わらせず今後もいろいろと関われたらとても嬉しいです.

ぱふぇるーと,楽しみにしています(*´꒳`*)♪

他のインターン生のブログ

リクルートのインターンで負けてきました - memo-mode

Recruit Holdings Winter Internshipに参加してきた - のほほんびより

Recruit Holdings Winter Internshipに参加してた - あみゅーの( ・´ー・`)どや

Recruit Holdings Winter Internshipに参加した - enkaismのブログ

Recruit Holdings Winter Internship 2015 に参加してきた - きくらげのブログ

Recruit Holdings Winter Internship Advent Calendar 28日目 - 日記

Recruit Holdings Winter Internship 2015 に参加しました - asdfghjkl