Skip to content

Instantly share code, notes, and snippets.

@mzp
Last active December 19, 2015 05:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mzp/5908217 to your computer and use it in GitHub Desktop.
Save mzp/5908217 to your computer and use it in GitHub Desktop.
Functional Ninja #3

Functional Ninja #3 (OCaml編 その2)

Functional Ninjaとは

ニンジャー

事前準備

こっちみてね: https://gist.github.com/mzp/5692680

当日やること

今日の目標

  • 特定キーワードを含むTweetをふぁぼるプログラムを書いてみましょう。
  • 単体テストは次の機会にしましょう(主催者都合)

型定義

OCamlは型が非常に強力なので、型だけである程度実装をしばることができます。 なので開発する場合は、まずは型から書きはじめることが多いです。 一部では 型駆動開発(TDD) と呼ばれていることが多いです。

例: https://twitter.com/bleis/status/173593199875600384

なので、まずは、 src/bot.mli に必要と思う型宣言を書いていきます。

(* botに必要な情報を格納する型(詳細は外部には公開しない) *)
type t

(* 初期化 *)
val init : unit -> t

(* キーワードで検索してTweetのリストを返す *)
val search : string -> t -> Twitter.Api_intf.Tweet.t list

(* 対象のTweetをふぁぼる *)
val fav : Twitter.Api_intf.Tweet.t -> t -> unit

コンパイル通したいので、 src/bot.ml に適当な実装を書きましょう。 assert false と書けば、 'a 型が作れるので、とりあえずコンパイルを通せます。(余談: AST上ではassert falseと assert condは別の要素らしいです。曰く、最適化をやりやすくするため。ただ実際に確認したわけじゃないので、そんなに信用しないでね)

type t = unit
let init   _ = assert false
let search _ = assert false
let fav    _ = assert false

あとは、src/ninja.ml から依存がないとコンパイルされないので、適当に書きます。

type t = Bot.t

コンパイルできることを確認しましょう。

$ make

動作確認の準備

utopでBotモジュールを動作を確認しましょう。 まずは動作確認に必要な設定を .ocamlinit に書きます。

(* twitter モジュールのロード *)
#require "twitter";;

(* ビルド結果を置いてあるロード対象に含める *)
#directory "_build/src";;

(* OCamltterのモジュールをロードする *)
#load "minOCamltter.cmo";;

(* Botモジュールをロードする *)
#load "bot.cmo";;

そして utop を起動します。 Botモジュールが扱えるので、いろいろ試してみましょう。

utop # Bot.init;;
- : unit -> Bot.t = <fun>

utop # Bot.init ();;
Exception: Assert_failure ("src/bot.ml", 2, 13).

initの実装

とりあえず型tはOauthでいいでしょう。 複数持たせたくなったら、レコードにしましょう。 tの中身は外部には公開していないので、わりと気楽に変えれます。

type t =
  Twitter.Oauth.t

init は、前回使ったOAuthの認証コードを流用します。

let init () =
  MinOCamltter.get_oauth ()

make してから動きを確認してみましょう。

utop # Bot.init ();;
- : Bot.t = <abstr>

searchの実装

次は、 search の実装です。

まずは、ユーティリティ関数を src/base.ml に移動させます。

let (+>) x f = f x

そいて、 .ocamlinit に追記します。

#require "twitter";;
#directory "_build/src";;
(* 追記 *)
#load "base.cmo";;
#load "minOCamltter.cmo";;
#load "bot.cmo";;

さて、 search 関数を実装します。 ちなみに ~count:count は ~count と略記できます。

let count = 100

let search str oauth =
  Twitter.Api11.Search.tweets oauth ~count str
  +> (function
     | `Ok s    -> s#statuses
     | `Error _ -> [])
  +> List.filter (fun x ->  x#retweeted_status = None)

さて make して、動作を確認しましょう。

utop # let t = Bot.init ();;
val t : Bot.t = <abstr>

utop # Bot.search "#functionalNinja" t;;
- : Twitter.Api_intf.Tweet.t list = [<obj>; <obj>; <obj>]

favの実装

fav 関数を実装する。

let fav tweet oauth =
  Twitter.Api11.Favorites.create oauth tweet#id
  +> ignore

実験しておきましょう。

utop # let t = Bot.init ();;
val t : Bot.t = <abstr>

utop # let xs = Bot.search "#functionalNinja" t;;
val xs : Twitter.Api_intf.Tweet.t list = [<obj>; <obj>; <obj>]

utop # Bot.fav (List.hd xs) t;;
- : unit = ()

src/ninja.mlの実装

まずは src/base.ml にユーティリティ関数を実装します。

let flip f x y = f y x

src/ninja.ml を実装します。

open Base

let () =
  let t =
    Bot.init ()
  in
  Bot.search "#functionalNinja" t
  +> List.iter (flip Bot.fav t)

実行してふぁぼられることを確認します。

$ make
$ ./ninja.byte

最後に、無限ループを作ってbotっぽくしましょう。

open Base

let run () =
  let t =
    Bot.init ()
  in
  Bot.search "#functionalNinja" t
  +> List.iter (flip Bot.fav t)

let () =
  while true do
    print_endline "wakeup";
    run ();
    Unix.sleep 60
  done

解答例

https://github.com/mzp/ninja_bot

参考文献


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment