Part2 : ErlangVMで動くRuby風の関数型言語Elixir(エリクサー) : 基礎編
Part1はこちら
インターフェース
defprotocolでinterfaceのようなものが定義できる
defprotocol Checker do def ok?(data) end
defimplでinterfaceの実装を定義できる
defimpl Checker, for: List do def ok?([]), do: "ng" def ok?([h | _]) when h == 1, do: "ok" def ok?([2 | _]), do: "ok2" end defimpl Checker, for: Number do def ok?(1), do: "ok" def ok?(_), do: "ng" end Checker.ok?([1,2,3]) # ok Checker.ok?([2,3,4]) # ok2 Checker.ok?([3,4,5]) # ng Checker.ok?(1) # ok Checker.ok?(2) # ng
関数をキャプチャ
定義済みの関数を、別の関数に渡したいとき等に。
f = &Integer.odd?/1 Enum.map([1,2,3,4,5], f) # [true, false, true, false, true]
Erlangの関数をコール
:(コロン)をつけるとErlangのモジュールが呼べる。
:timer.sleep 1000 # 1秒sleep
プロセス生成
spawnで生成。receiveで外から値を受け取れる。
defmodule Process do def from do receive do i -> IO.inspect i * i after 100 -> IO.inspect "timeout.." end end end p = spawn Process, :from, [] p <- 2 # プロセスに値を送るとreceiveで受け取れる。
Stream
scalaにもある無限のリストを作るコレクション
IO.inspect Stream.map(1..1000000000, &(&1 * &1)) |> Enum.take(5) # [1, 4, 9, 16, 25]
File
ファイルをread。
case File.read("/etc/hosts") do { :ok, body } -> IO.inspect body { :error, reason } -> IO.inspect reason end
!を付けると例外を飛ばすようになる。rubyっぽい...
body = File.read!("/etc/hosts") IO.inspect body
例外をcatchしてみる
try do File.read! "/etc/hogeeeee" rescue File.Error -> IO.inspect "catche!" end
様々なファイル操作の関数がある
File.rm_rf! "/tmp/fuga" File.mkdir! "/tmp/fuga" File.write! "/tmp/fuga/elixir.txt", "elixir!" IO.puts File.read! "/tmp/fuga/elixir.txt" IO.inspect File.ls! "/tmp/fuga" File.rm! "/tmp/fuga/elixir.txt"
pmap
Elixir関係ないけど、便利なErlangの関数だったので。
1ループを1プロセスで実行するmap。ものすごい並列感...
defmodule Par do def exec(i) do :timer.sleep 1000 i * i end end :rpc.pmap {Par, :exec}, [], [1,2,3,4,5,6,7,8,9] # 1sかかる Enum.map [1,2,3,4,5,6,7,8,9], &Par.exec/1 # 9sかかる
公式サイトを翻訳したほうが早いんじゃないかと思えてきた...
ErlangVMで動くRuby風の関数型言語Elixir(エリクサー) : 基礎編
Erlangの奇妙な文法を覚えることなく、Erlangの機能が使えるエリクサー。
Ruby風の文法なのでRubyistには馴染みの関数などが使えるが、以下のことを頭にいれておくべし。
楽天Technologyカンファレンスでセッションがあったり、Erlangを簡単に書ける言語としてこれから普及していくのではないでしょうか!
インストール
brew install elixir
iex
iex
irbのようにiexで、試し書きが簡単にできます。(iexはerlのラッパー)
基本文法
四則演算
1 + 1 # 2 1 - 1 # 0 1 * 1 # 1 1 / 1 # 1.0 div 1, 1 # 1 rem 2, 4 # 2 # 2 % 4 not work
型
"a" # string 1 # integer 1.2 # float :id # atom [1,2,3] # list {1,2,3} # tuple [a: "a", b: 2] # keyword
文字列
s = "str" "[" <> s <> "]" # [str] "[#{s}]" # [str] String.reverse "str" # rts String.slice "str", 1, 2 # tr String.first "str" # f String.last "str" # r String.at "str", 1 # t String.graphemes("str") # ["s", "t", "r"]
変数
v = "v" # v = "vv" 不変なので代入不可
デバッグ
IO.inspect [1,2,3] IO.puts "foo" # 文字列のみ(改行コード付き) IO.write "foo" # 文字列のみ(改行コードなし)
コレクション
[head | tail] = [1,2,3] head # 1 tail # [2,3 Enum.map [1,2,3], fn(i) -> i * i end # [1,4,9] Enum.map [1,2,3], &(&1 * &1) # [1,4,9] Enum.filter [1,2,3], &(Integer.odd?(&1)) # [1,3] Enum.find [1,2,3], &(&1 == 1) # 1 Enum.all? [1,3], &(Integer.odd?(&1)) # true [scala = forall] Enum.any? [1,3], &(Integer.odd?(&1)) # true [scala = exists] Enum.at [1,2,3], 1 # 2 Enum.max [1,2,3] # 3 Enum.join [1,2,3], "-" # 1-2-3 f = fn i when rem(i, 2) == 0 -> [i] _ -> [] end Enum.flat_map(1..10, f) # [2, 4, 6, 8, 10] Enum.map(1..10, f) |> List.flatten # [2, 4, 6, 8, 10]
パイプ
[1,2,3,4,5] |> Enum.filter(&(rem(&1,2) != 0)) |> Enum.map(&(&1 * &1)) # [1,9,25] "str" |> String.graphemes |> Enum.map(&("-#{&1}-")) |> Enum.join # "-s--t--r-"
気軽に繋げて書くことができる。
条件
if
if true do "ok" # ok else "ng" end # 簡易版 if true, do: "ok", else: "ng" # ok if true, do: 1 # 1 if false, do: 1, else: 2 # 2
unless
unless false, do: "unless"
case
case 1 do 1 -> p "ok" # ok _ -> p "ng" end case [1,2,3] do [1 | _] -> p "ok" # ok _ -> p "ng" end case {1,2,3} do {1,2,3} -> p "ok" # ok _ -> p "ng" end case [1,2,3] do [_ | [2,3]] -> p "ok" _ -> p "ng" end case [1] do [h | []] -> p h # 1 _ -> p "ng" end a = 1 case true do ^a -> p "not match" _ -> p "match" end
関数
基本
defmodule Math do def sum(a, b) do a + b end end Math.sum(1,2) # 3 defmoduleの中でないとdefは定義できない Math.sum 1,2 # rubyのように()省略可 # ネスト defmodule Math do defmodule Foo do def sum(a, b) do a + b end end end Math.Foo.sum 1,2 # 3 # 短く defmodule Math do def sum(a, b), do: a + b end Math.sum 1, 2 # 3
匿名関数
f = fn(x, y) -> x + y end f.(1,2) # 3 # 短く f = &(&1 + &2) f.(1,2) # 3
高階関数
ff = fn(f) -> f.(2,3) + f.(4,5) end ff.(f) # 14
ガード
f2 = fn x when x < 1 -> "ng" _ -> "ok" end f2.(1) # ok
サンプル
# フィボナッチ defmodule Fib do def f(l), do: f(l,1,1) def f(l, a, b) do if l > b, do: [b] ++ f(l, b, a + b), else: [] end end Fib.f(30) # [1, 2, 3, 5, 8, 13, 21]
# リバース defmodule Reverse do def r([head | tail]), do: r(tail) ++ [head] def r([]), do: [] end Reverse.r [1,2,3] # [3,2,1] Enum.reverse [1,2,3] # [3,2,1]
クラスのようなもの
defrecord Person, name: "", age: 0 do def say(mine) do "I am " <> mine.name <> "." end end tanaka = Person.new(name: "tanaka", age: 20) tanaka.say # I am tanaka.
AS3で動画を扱ったり、JSと連携したり
ActionScript3で動画に関するメモ
動画再生
動画を再生するには以下のいずれか。
- FLVPlaybackを使う。
- flexのspark.components.VideoPlayerを使う。
- Videoクラスなどを使い自前で作る。
1がスタンダード。
FLVPlayback
使い方
ウインドウ -> コンポーネント -> Videoを開く -> FLVPlaybackをステージに。
import fl.video.FLVPlayback; var player = new FLVPlayback(); addChild(player); player.source = "hoge.mp4"; player.autoPlay = true;
対応してる種類
デフォルトで用意されてるコンポーネント。flvと名前が付いてるが、mp4など色々いける。
Flash Player 9 Update 3 では、業界標準の H.264 エンコーディングを利用する高解像度の MPEG-4 ビデオ形式がサポートされ、FLVPlayback コンポーネントの機能が向上しています。サポートされる形式は、MP4、M4A、MOV、MP4V、3GP および 3G2 です。
スキン
Macだと以下にスキンがある。同じ位置にflaもあるので自由にカスタマイズできる。
"/Applications/Adobe Flash CC/Adobe Flash CC.app/Contents/Common/Configuration/FLVPlayback Skins/MinimaSilverPlayBackSeekCounterVolMute.swf"
動画に字幕を付ける
字幕を付けるにはFLVPlaybackCaptioningが使える。
var captions = new FLVPlaybackCaptioning(); captions.autoLayout = false; captions.flvPlayback = player_mc.player; captions.source = "sample.xml";
残念なことxmlファイルを読み込んでしか使えない...。
xmlオブジェクトを読めるようにしてくれれば...。
JSとFlashの連携
連携に必要な設定
1. allowScriptAccessをalwaysに設定する必要がある
<object ... > <param name="allowScriptAccess" value="always"> </object>
2. file://だとダメ
http://localhost/ とかでないと、動きません。
3. flashとjsが別ドメインの場合
flash.system.Security.allowDomain('example.com')
js -> flash
html
<object id="hoge" ...>
js
var swf = document.hoge || window.hoge; swf.to_flash();
flash
if (ExternalInterface.available) { ExternalInterface.addCallback('to_flash', function() { test.text = 'receive it!'; }); }
Scalaでフィボナッチ数列のメモ
なんか面接とかでたまに書かされるらしいので
Listで再帰
// ListFib.f(100) object ListFib { def f(lim: Int): List[Int] = { 1 :: 2 :: f(1, 2, lim) } def f(a: Int, b: Int, lim: Int): List[Int] = if (b > lim) { Nil } else { b :: f(b, a + b, lim) } }
Streamで再帰
// StreamFib.f().take(10).toList object StreamFib { def f(a: Int = 1, b: Int = 1): Stream[Int] = b #:: f(b, a + b) }
文字列内の数値をインクリメントする
/** * 文字列内の数値をインクリメントする * ex) TextIncrementer("abc123efg456hij").++() // abc124efg457hij * ex) TextIncrementer("あいう1 2 3えお").++() // あいう2 3 4えお * @param t インクリメントしたい文字列 * @param incrValue いくつインクリメントするか * @return インクリメントした文字列 */ case class TextIncrementer(t: String, incrValue: Int = 1) { def ++(i: Int = 0, n: String = ""): String = { n match { case "" => ++(1, t.slice(0, 1)) case _ if (i > t.length) => n // end case _ => { //println(i, n) n.takeRight(1).exists(_.isDigit) match { // abc1 1が数値か case true => { val c = sequenceNumberCount(i) val nn = n.dropRight(1) // abc1 -> abc val inc = t.slice(i - 1, c).toInt + incrValue ++(c + 1, nn + inc + t.slice(c, c + 1)) } case false => ++(i + 1, n + t.slice(i, i + 1)) } } } } private def sequenceNumberCount(i: Int): Int = { t.slice(i, i + 1).exists(_.isDigit) match { case true => sequenceNumberCount(i + 1) case false => i } } }
オートマトンにしようかと思ったけどまぁ...
正規表現版
"hoge1 = 9999;".gsub(/\d+/) {|i| i.to_i + 1 } // "hoge2 = 10000;"
Fluentdまとめ
Fluentdとは?
ログを収集・集約するruby製のミドルウェアです。いままではrsyncやscpでログを全サーバーから回収したりしてたと思いますが、fluentdを使えば便利に色んな方法でログを集めることができます。
仕組み
Fluentdは、Input・Buffere・Outputというモジュールで構成されています。
- Input
- ログファイル・TCP・UNIXドメインソケット等で受け取ります。
- Buffer
- 受け取ったログを一時的に保管しておくところです。ファイルに保管するかメモリに保管するか選べます。
- Outputは
- Bufferに積まれたログをファイル・MySQL・HBase・MongoDBなどに書いたりします。別サーバーのFluentdに渡すこともできます。
概念図
インストール
yumリポジトリ設定
vi /etc/yum.repos.d/td.repo cat /etc/yum.repos.d/td.repo [treasuredata] name=TreasureData baseurl=http://packages.treasure-data.com/redhat/$basearch gpgcheck=0
インストール
yum install td-agent
準備
mkdir /var/log/fluent chown td-agent:td-agent /var/log/fluent
起動
service td-agent start
送信側サーバー
vi /etc/td-agent/td-agent.conf <source> type forward # TCPで受け取る指定 tag app.file # ここのtagが<mtach>で使われる </source> <match app.file> type forward host 192.168.56.101 # 集約サーバーのhost flush_interval 1s </match>
受信側サーバー
vi /etc/td-agent/td-agent.conf <source> type forward # 送信側サーバーから受け取る(TCP)指定 tag app.file </source> <match app.file> type file # ファイルに書く指定 path /tmp/fluentoo.log </match>
気をつけるパラメータ
buffer_chunk_limit buffer_queue_limit
chunkにログがたまります。
queueにchunkがたまります。
以下の設定だと、2Gなのでサーバーのメモリはそれ以下にしないといけない。
buffer_chunk_limit 8m buffer_queue_limit 256 8MB * 256個 = 2048MB
パフォーマンス
てきとうにVM1からVM2に書くだけのシンプルな環境で計測してみた。
どっちのVMも2coreのメモリ4GのCentOS6。
- 1ログ100Byteを100万回書いた。
- 10,000QPSくらいでた。
- 1書き込み1msもかからない。
- 1ログ30KBを5万回書いた。
- 1500QPSほど。
- 1書き込み1msかからない。
非同期で書くのでかなり速い。
情報
- http://www.slideshare.net/hotchpotch/20120204fluent-logging
- http://www.slideshare.net/tagomoris/oss-nhntech
- http://www.slideshare.net/baguzy/fluentd-meetup-2-14073930
- http://d.hatena.ne.jp/mikeda/20120704/1341363870
http://docs.fluentd.org/articles/quickstart
- ここのチャットで、日本語で気軽にFluentd開発者に質問できます。
まとめ
きっとこれからは各種ログ(/var/log)はFluentdで収集するようになるんだろうなぁと。
グラフ化もGrowthForecastで簡単にできるので、データの見える化が簡単にできますね。
Hbaseへ複雑に書く場合は、rubyだとHbaseのjarを呼んだりしないといけないので同じログ収集ツールであるJava製のFlumeを使うといいです。
有名フレームワークのCSRF対策方法を調べたまとめ
ZendFramework
流れ
- 表示時 : token生成→hiddenセット + セッションにセット
- 送信時 : 送られてきたtokenをセッションにあるものと同じかでチェック
token生成方法
ランダム値 + salt + 固定値 + ランダム値
md5( mt_rand(1,1000000) . $this->getSalt() . $this->getName() . mt_rand(1,1000000) );
まとめ
ランダム値をセッションにいれて、送られてきたものとチェック。
cakephp
流れ
- 表示時 : token生成→hiddenセット + セッションにセット
- 送信時 : 送られてきたtokenをセッションにあるものと同じかでチェック。有効期限設定可。
まとめ
ランダム値をセッションにいれて、送られてきたものとチェック。
fuelphp
流れ
- 表示時 : token生成→hiddenセット + クッキーにセット
- 送信時 : 送られてきたtokenをクッキーにあるものと同じかでチェック。有効期限設定可(=クッキーの有効期限)。
まとめ
jsにも対応してるのでjsでクッキー書いたりしたいからクッキー使ってるみたい。
ruby on rails
流れ
- 表示時 : token生成→hiddenセット + セッションにセット
- 送信時 : 送られてきたtokenをセッションにあるものと同じかでチェック
まとめ
ランダム値をセッションにいれて、送られてきたものとチェック。
結論
あいだをとって
- ランダム値をセッションにいれて、送られてきたものとチェック
- 有効期限チェック
- salt + shha1で生成
が一番よさそう。つまりcakeのが一番よさそう。