再帰

cf. 9 再帰 – Recursion – Elixir

Elixir では繰り返しを再帰で行う。例えば、文字列を n 回出力するスクリプトはこんな風になる。

defmodule Recursion do

  def print_multiple_times(msg, n) when n <= 1 do
    IO.puts msg
  end
  def print_multiple_times(msg, n) do
    IO.puts msg
    print_multiple_times(msg, n - 1)
  end

end

Recursion.print_multiple_times("Hello!", 3)
^o^ > elixir recursion.exs
Hello!
Hello!
Hello!

リストの場合には [head|tail] と、終端条件には [] (空リスト)を使えばいい。

defmodule Math do

  def sum_list([], accum) do
    accum
  end

  def sum_list([head|tail], accum) do
    sum_list(tail, head + accum)
  end

end

IO.puts Math.sum_list([1, 2, 3], 0)
^o^ > elixir sum_list.exs
6

再帰についてはこれくらいでいいだろう。

モジュールとコンパイル,スクリプト

cf. 8 モジュール – Modules – Elixir

モジュール

Elixir では関数をモジュールの中に入れてグループ化する。モジュールは defmodule マクロで定義し、その中で def マクロを使って関数を定義する。またマクロって単語が出てきたけど気にしない。

iex(1)> defmodule Math do
...(1)>   def sum(a, b) do
...(1)>     a + b
...(1)>   end
...(1)> end
{:module, Math,
 <<70, 79, 82, 49, 0, 0, 4, 200, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 157,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:sum, 2}}
iex(2)> Math.sum(1, 2)
3

モジュールの関数を呼び出すときは「モジュール名.関数名」だな。

コンパイル

モジュールをファイルに書いて保存しておき、コンパイルすると再利用しやすくなる。例えば次のようなファイルを作る。拡張子は ex。

defmodule Math do

  def sum(a, b) do
    a + b
  end

end

このソースファイルは elixirc コマンドで Erlang VM のバイトコードにコンパイルできる。

^o^ > elixirc math.ex

^o^ > ls
Elixir.Math.beam  hello.exs  math.ex

Elixir.Math.beam っていうファイルが、コンパイルされたバイトコード。ここで iex を立ち上げると、Math モジュールが使えるようになる。カレントディレクトリのバイトコードは自動的に読み込まれるってわけだ。

^o^ > iex
Eshell V8.0  (abort with ^G)
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Math.sum(1, 2)
3

スクリプトモード

コンパイルを必要としないのがスクリプトモード。こないだの記事で Hello, world プログラムを作ったのがこれ。こっちは拡張子を exs にする。ex ファイルと exs ファイルは中身的には区別はなく、違うのは目的。ex はコンパイルされるのを意図しているのに対して、exs はコンパイルせずにスクリプトモードで使用する。

defmodule Math do

  def sum(a, b) do
    a + b
  end

end

IO.puts Math.sum(1, 2)

上のスクリプト math.exs を実行すると次のように出力される。おっと、さっき作った Elixir.Math.beam を消しておくのを忘れないように。そうでないと「Math モジュールを再定義しようとしてる」とかいう意味(たぶん)の warning が出る。

^o^ > rm Elixir.Math.beam

^o^ > elixir math.exs
3

Elixir スクリプトの実行は elixir コマンドね。

名前付き関数

モジュールの中で、def/2 を使って関数を定義するのは上でもやった。この関数はモジュールの外からも呼べる関数になる。一方、defp/2 を使って定義するとモジュールの外からは呼べないプライベート関数になる。これらは名前付き関数と呼ばれる、たぶん、文脈からすると。

defmodule Math do

  def sum(a, b) do
    a + b
  end

  defp do_sum(a, b) do
    a + b
  end

end

IO.puts Math.sum(1, 2)
IO.puts Math.do_sum(1, 2)

上のスクリプトでは、関数 sum とプライベート関数 do_sum を定義して、呼び出している。これを実行すると:

^o^ > elixir math2.exs
warning: function do_sum/2 is unused
  math2.exs:6

3
** (UndefinedFunctionError) function Math.do_sum/2 is undefined or private
    Math.do_sum(1, 2)
    math2.exs:13: (file)
    (elixir) lib/code.ex:363: Code.require_file/2

こうなる。
Elixir の関数宣言(宣言て書いてあるな)は、ガードと複数句に対応している。例えばこんなふうに:

defmodule Math do

  def zero?(0) do
    true
  end

  def zero?(x) when is_number(x) do
    false
  end

end

IO.puts Math.zero?(0)
IO.puts Math.zero?(1)
^o^ > elixir math3.exs
true
false

関数のキャプチャ

関数を値として取り出すことをキャプチャと言い、& を頭につける。math.exs を iex で使ってみよう。

^o^ > iex math.exs
Eshell V8.0  (abort with ^G)
3
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> fun = &Math.sum/2
&Math.sum/2
iex(2)> fun.(1, 3)
4

キャプチャ構文は関数を作るときのショートカットとしても使える。この場合 &1 は1番目の引数を意味する。

iex(3)> fun = &(&1 + 1)
#Function<6.52032458/1 in :erl_eval.expr/5>
iex(4)> fun.(2)
3

デフォルト引数

名前付き関数はデフォルト値を持つ引数をとることができる。

defmodule Concat do

  def join(a, b, sep \\ " ") do
    a <> sep <> b
  end

end

IO.puts Concat.join("hello", "world")
IO.puts Concat.join("hello", "world", "-")
^o^ > elixir concat.exs
hello world
hello-world

キーワードリスト,マップ,ディクショナリ

 cf. 7 キーワード,マップそしてリスト – Keywords, maps and dicts – Elixir

Elixir には、キーワードリストとマップという2つの連想データ構造がある。

キーワードリスト

2要素のタプルのリストのうち、タプルの1つ目の要素(つまりキー)がアトムであるような構造を Elixir ではキーワードリストと呼んでいる。

iex(1)> list = [{:a, 1,}, {:b, 2}]
[a: 1, b: 2]

返り値にあるように、キーワードリストには [a: 1, b: 2] のような記法がある。

iex(2)> list == [a: 1, b: 2]
true

要素にアクセスするには [] にキーを指定する。

iex(3)> list[:a]
1

キーワードリストはあくまでもリストなので、例えば ++ で連結できる。

iex(4)> [a: 0] ++ list
[a: 0, a: 1, b: 2]

上のように、キーが重複しても構わない。もっともこれの何がうれしいのか。
キーが複数あるとき、値を得ようとすると前にある値を返すことに注意。

iex(5)> list = [a: 0, a: 1, b: 2]
[a: 0, a: 1, b: 2]
iex(6)> list[:a]
0

キーワードリストの重要な特徴は2つ:

  • キーの順序を開発者が指定したとおりに保持する
  • キーを複数回与えることができる

この特徴のため、キーワードリストは関数へオプションを渡すためのデフォルトのメカニズムになっている。例えば、if/2 はこんな書き方もできる。

iex(7)> if(false, [do: :this, else: :that])
:that

一般に、キーワードリストは引数の最後にあるので、角カッコはつけなくてもいい。

マップ

マップは Ruby でいうところの Hash だ。

iex(8)> map = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
iex(9)> map[:a]
1
iex(10)> map[2]
:b

マップの特徴は:

  • キーをどんな値にもできる
  • キーは順番通りにならない

マップのキーがすべてアトムの場合には、キーワードリストと似たような構文(キーワード構文)を使うことができる。

iex(11)> map =%{a: 1, b: 2}
%{a: 1, b: 2}

パターンマッチングもできる。

iex(12)> %{} = %{a: 1, b: 2}
%{a: 1, b: 2}
iex(13)> %{a: a} = %{a: 1, b: 2}
%{a: 1, b: 2}
iex(14)> a
1
iex(15)> %{c: c} = %{a: 1, b: 2}
** (MatchError) no match of right hand side value: %{a: 1, b: 2}

マップのパターンマッチングは、部分的にマッチする。だからからのマップはすべてのマップにマッチする一方、存在しないキーにマッチさせようとするとエラーになる。
アトムのキーにはアクセスするための特別の構文が用意されている。

iex(15)> map = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
iex(16)> map.a
1

更新のための構文もある。

iex(17)> %{map | :a => 2}
%{2 => :b, :a => 2}
iex(18)> %{map | :c => 3}
** (KeyError) key :c not found in: %{2 => :b, :a => 1}
    (stdlib) :maps.update(:c, 3, %{2 => :b, :a => 1})
    (stdlib) erl_eval.erl:255: anonymous fn/2 in :erl_eval.expr/5
    (stdlib) lists.erl:1263: :lists.foldl/3

更新するキーは存在するキーでなければいけない。存在しないキーに対して更新しようとするとエラーになる。

ディクショナリ

ディクショナリはインターフェイスのようなもの(Elixir ではビヘイビアと呼ぶ)で、キーワードリストもマップもこのインターフェイスを実装している。

iex(18)> keyword = []
[]
iex(19)> map = %{}
%{}
iex(20)> Dict.put(keyword, :a, 1)
[a: 1]
iex(21)> Dict.put(map, :a, 1)
%{a: 1}

このように、Dict モジュールを使うとキーワードリストとマップを同じように扱える。

バイナリ,文字列,文字リスト

 cf. 6 バイナリ,文字列そして文字リスト – Binaries, strings and char lists – Elixir

バイナリ

バイナリは Elixir のデータの一つでビット列……もっと正確に言うとバイト列だ。バイナリは << >> で表すことができる。

iex(1)> x = <<0, 1, 2, 3>>
<<0, 1, 2, 3>>
iex(2)> is_binary x
true

バイナリが何バイトあるかは byte_size/1 で求められる。

iex(3)> byte_size x
4

バイナリの連結は <> 演算子。文字列の連結演算子と同じだけど、その理由は後から出てくる。

iex(4)> <<1, 2>> <> <<3, 4>>
<<1, 2, 3, 4>>

文字列

Elixir において、文字列は特殊なバイナリだ。

iex(5)> is_binary "hello"
true

特殊、とはどういうことかというと、そのバイナリが UTF-8 で解釈できるか(エンコードできるか)ということだ。だから次のような例では、バイナリを作っても文字列が返ってくる。

iex(6)> <<104, 101, 108, 108, 111>>
"hello"

逆に文字列のバイナリ表現を見るには、ヌルバイト <<0>> を連結してみるのが一般的らしい。

iex(7)> "hello" <> <<0>>
<<104, 101, 108, 108, 111, 0>>

パターンマッチング

バイナリもパターンマッチングできる。バイナリのパターンはちょうど1バイト(8ビット)ずつを期待していることに注意。

iex(8)> <<1, x, 3>> = <<1, 2, 3>>
<<1, 2, 3>>
iex(9)> x
2
iex(10)> <<1, 2, x :: binary>> = <<1, 2, 3, 4>>
<<1, 2, 3, 4>>
iex(11)> x
<<3, 4>>

2番目の例では、x に「残り」のバイナリが束縛される。

文字リスト

"(二重引用符)は文字列を作る。これに対して '(単一引用符)は文字リストを作る。

iex(12)> 'hello'
'hello'
iex(13)> is_list 'hello'
true

文字列と文字リストは別のもの。

iex(14)> 'hello' == "hello"
false

文字リストから文字列に変換するには to_string/1 が使える。

iex(15)> to_string 'hello'
"hello"

補足

このエントリでは、関数呼び出しをカッコなしで書いた: is_binary x のように。Elixir では関数呼び出しの引数を囲むカッコは必須ではない。

case, cond, if, unless

 cf. 5 case,condそしてif – case, cond and if – Elixir

case

case は1つの値をいずれかの句(clause)にマッチするまで、複数の句と比較する。

iex(1)> case {1, 2, 3} do
...(1)>   {4, 5, 6} -> "This clause won't match"
...(1)>   {1, x, 3} -> "This clause will match and bind x to 2 in this clause"
...(1)>   _ -> "This clause would match any value"
...(1)> end
"This clause will match and bind x to 2 in this clause"

上の例では、2番目の句がマッチして、変数 x に値 2 が束縛されている。この変数 x は句の中だけで有効らしい。
もし、すでにある変数にマッチさせたい場合は ^ (ピン演算子)を使う。

iex(2)> x = 1
1
iex(3)> case 10 do
...(3)>   ^x -> "Won't match"
...(3)>   _  -> "Will match"
...(3)> end
"Will match"

句にはガード(guard)をつけることで、条件を追加することができる。

iex(4)> case {1, 2, 3} do
...(4)>   {1, x, 3} when x > 0 -> "Will match"
...(4)>   _ -> "Won't match"
...(4)> end
"Will match"

ガードに使える式には制限があるけど、詳しくはリンク先参照。制限といっても普通に使う分には困らない程度に見える。
ガードで起きたエラーは、単にガードの失敗とみなされて外には影響しない。次の例では、hd(x)x がリストではないのでエラーになってるはずだけど、case 全体としてはエラーにならない。

iex(5)> case 1 do
...(5)>   x when hd(x) -> "Won't match"
...(5)>   x -> "Got: #{x}"
...(5)> end
"Got: 1"

もし、どの句にもマッチしなければエラーになる。

iex(6)> case :ok do
...(6)>   :error -> "Won't match"
...(6)> end
** (CaseClauseError) no case clause matching: :ok

匿名関数

ちょっと話がそれるけど、匿名関数も句やガードを持つことができる。

iex(6)> f = fn
...(6)>   x, y when 0 > x -> x + y
...(6)>   x, y -> x * y
...(6)> end
#Function<12.52032458/2 in :erl_eval.expr/5>
iex(7)> f.(1, 3)
3
iex(8)> f.(-1, 3)
2

cond

Scheme の cond と同じ。

iex(9)> cond do
...(9)>   2 + 2 == 5 -> "This will not be true"
...(9)>   2 * 2 == 3 -> "Nor this"
...(9)>   1 + 1 == 2 -> "But this will"
...(9)> end
"But this will"

もし、true になる条件がなければエラーになる。

iex(10)> cond do
...(10)>   2 + 2 == 5 -> "This is never true"
...(10)>   2 * 2 == 3 -> "Nor this"
...(10)> end
** (CondClauseError) no cond clause evaluated to a true value

ifとunless

試したい条件が1つだけの時は、ifunless マクロが使える。……なんか「マクロ」って単語がさらっと出てきたぞ。

iex(10)> if true do
...(10)>   "This works"
...(10)> end
"This works"
iex(11)> unless true do
...(11)>   "This will never be seen"
...(11)> end
nil

else も使える。

iex(12)> if nil do
...(12)>   "This won't be seen"
...(12)> else
...(12)>   "This will"
...(12)> end
"This will"

doブロック

if は次のようにも書ける。

iex(13)> if true, do: 1 + 2
3

Elixir では do ~ end は、式のまとまりを do: へ渡す役割をしている。次の2つの例は同じことをしている。

iex(14)> if true do
...(14)>   a = 1 + 2
...(14)>   a + 10
...(14)> end
13
iex(15)> if true, do: (
...(15)>   a = 1 + 2
...(15)>   a + 10
...(15)> )
13

2つ目の構文は「キーワードリストを使っている」と言う。もちろん else も使える。

iex(16)> if false, do: :this, else: :that
:that

パターンマッチング

 cf. 4 パターンマッチング – Pattern matching – Elixir

マッチ演算子

= は変数への束縛に使われる。

iex(1)> x = 1
1
iex(2)> x
1

けれども、Elixir では = はマッチ演算子と呼ばれる。なぜなら、本質的には左辺と右辺のマッチングに使われるから。変数への束縛は、変数が左辺にあって右辺とマッチした時にだけ起こる。

iex(3)> 1 = x
1
iex(4)> 2 = x
** (MatchError) no match of right hand side value: 1

1 = x という式が Elixir では妥当な式であることに注意(何の役に立つのかわからないけど)。2つ目の例では左辺と右辺がマッチしないのでエラーが起きている。

パターンマッチング

マッチ演算子はもっと複雑なデータにも使える。例えばタプルにも。

iex(5)> {a, b, c} = {1, 2, 3}
{1, 2, 3}
iex(6)> a
1
iex(7)> b
2
iex(8)> c
3

このパターンマッチングは左辺と右辺が対応していないとエラーになる。タプルのサイズが違うとか、異なるデータ型だとかいう場合だ。

iex(9)> {a, b, c} = {:hello, "world"}
** (MatchError) no match of right hand side value: {:hello, "world"}

iex(9)> {a, b, c} = [1, 2, 3]
** (MatchError) no match of right hand side value: [1, 2, 3]

特定の値にマッチさせることもできる。次の例では、タプルの最初の値が :ok の時だけマッチする。

iex(9)> {:ok, result} = {:ok, "hello"}
{:ok, "hello"}
iex(10)> result
"hello"
iex(11)> {:ok, result} = {:error, "hello"}
** (MatchError) no match of right hand side value: {:error, "hello"}

リストでは headtail というパターンマッチができる。

iex(11)> [head | tail] = [1, 2, 3]
[1, 2, 3]
iex(12)> head
1
iex(13)> tail
[2, 3]

ちょっと話題がずれるけど、この [head|tail] という形式は、リストの先頭に値を追加するのにも使える。

iex(14)> list = [1, 2, 3]
[1, 2, 3]
iex(15)> [0 | list]
[0, 1, 2, 3]

ピン演算子

Elixir の変数は再束縛することができる。

iex(16)> x = 1
1
iex(17)> x = 2
2
iex(18)> x
2

ピン演算子 ^ は、束縛済みの値とマッチングさせたいときに使う。

iex(19)> x = 1
1
iex(20)> ^x = 2
** (MatchError) no match of right hand side value: 2

パターンの中の値が何でもいい時には _ にマッチさせる。

iex(20)> {x, _, y} = {1, 2, 3}
{1, 2, 3}
iex(21)> x
1
iex(22)> y
3

今日はここまで。

基本的な演算子

 cf. 3 基本的な演算子 – Basic operators – Elixir

算術演算子

四則演算は一通りある。

  • +
  • -
  • *
  • /

普通と違うのは、昨日も書いたけど / が必ず浮動小数点数を返すってこと。

リストの操作

これも昨日書いた。

  • ++
  • --

文字列の連結

<> 演算子。

iex(1)> "foo" <> "bar"
"foobar"

論理演算子

論理演算子は6種類。

  • and
  • or
  • not
  • &&
  • ||
  • !

andornot は第1引数に真理値を要求する。

iex(2)> true and true
true
iex(3)> false or is_atom(:hello)
true
iex(4)> not true
false
iex(5)> 1 and true
** (ArgumentError) argument error: 1

最後の例のように、真理値でない場合はエラーが発生する。
それに対して、&&||! は真理値でなくても構わない。この場合、falsenil 以外はすべて真とみなされる。

iex(5)> 1 && true
true
iex(6)> false || 11
11
iex(7)> !nil
true

比較演算子

  • ==
  • ===
  • !==
  • !===
  • >=
  • <=
  • >
  • <

大体意味はわかる。== と === の違いは整数と浮動小数点数の違いに厳密かどうかってこと。

iex(8)> 1 == 1.0
true
iex(9)> 1 === 1.0
false

異なる型を比較することもできる。

iex(10)> 1 < :atom
true

型の大小関係は正確に覚えておく必要はないと書いてあるので省略。

基本的なデータ型

 cf. 2 基本的な型 – Basic types – Elixir

数値

数値型には整数と浮動小数点数がある。

iex(1)> 1 + 2
3
iex(2)> 5 * 5
25
iex(3)> 10 / 2
5.0

/ 演算子は必ず浮動小数点数を返す。整数の商や余りが必要なら、関数 div と rem が使える。

iex(4)> div(10, 2)
5
iex(5)> rem(10, 3)
1

真理値

truefalse が真理値。

iex(6)> true
true
iex(7)> true == false
false

関数 is_boolean/1 で真理値かどうかを判定できる。

iex(8)> is_boolean(false)
true
iex(9)> is_boolean(1)
false

アトム

アトムとは、それ自身の名前が値を表している定数。Ruby のシンボルのようなもの。

iex(10)> :hello
:hello
iex(11)> :hello == :world
fals

真理値 true と false も実はアトム。

iex(12)> is_atom(true)
true
iex(13)> is_atom(false)
true
iex(14)> true == :true
true

文字列

文字列は ” (二重引用符)で囲む。

iex(15)> "hello"
"hello"
iex(16)> "hello
...(16)> world"
"hello\nworld"

一昨日も書いたけど、文字列の中に自然に改行を入れられるのが新鮮。エスケープシーケンスも使える。

iex(17)> "hello\nworld"
"hello\nworld"

Ruby みたいな文字列の埋め込みもできる。

iex(18)> "hello #{:world}"
"hello world"

文字列を出力するには IO モジュールの IO.puts/1 関数。

iex(19)> IO.puts "hello"
hello
:ok

匿名関数

キーワード fn で始まり end で終わる。

iex(20)> add = fn a, b -> a + b end
#Function<12.52032458/2 in :erl_eval.expr/5>

Elixir では関数は”第一級市民”で、ほかのデータ型のように変数に代入することができる。is_function/1 に関数を渡すと true が返ってくる。fnend で作った関数を呼び出すには、関数名と引数リストの間に . が必要。

iex(21)> is_function(add)
true
iex(22)> add.(1, 3)
4

(連結した)リスト

リストは [] で囲む。リストにはどんな型でも含めることができる。

iex(23)> [1, 2, 3]
[1, 2, 3]
iex(24)> [1, "hello", :world]
[1, "hello", :world]

++ で連結、– で差を得ることができる。

iex(25)> [1, 2, 3] ++ [4, 5, 6]
[1, 2, 3, 4, 5, 6]
iex(26)> [1, 2, 1, 3] -- [1, 3]
[2, 1]

差といっても、全部が引かれるわけではないんだな。これはちょっとハマりどころかも。
hdtl は Scheme でいう carcdr

iex(27)> hd([1, 2, 3])
1
iex(28)> tl([1, 2, 3])
[2, 3]

タプル

タプルは { と } で囲む。

iex(29)> {:ok, "hello"}
{:ok, "hello"}
iex(30)> tuple_size({:ok, "hello"})
2

関数 put_elem/3 を使うと、タプルのインデックスに新しい値を割り当てることができる。

iex(31)> tuple = {:ok, "hello"}
{:ok, "hello"}
iex(32)> put_elem(tuple, 1, "world")
{:ok, "world"}
iex(33)> tuple
{:ok, "hello"}

put_elem/3 は新しい値を返すことに注意。Elixir の値は変更不可なので、上のように変数 tuple の値は変わらない。

補足

関数の名前を書くのに foo/1 のような書き方をする。これは関数 foo が引数を1つとることを表している。Elixir では引数の数の異なる、同名の関数を定義できるらしい。

Elixirにさわってみた

関数型言語の Elixir(エリクサー)にさわってみた。ネットの情報を見た感じだと、Ubuntu にインストールするのはちょっと面倒そうだったので、こないだ買った新しいノートPC(windows10)で。
Elixir は Erlang の VM 上で動作する言語。Erlang の特徴を引き継ぎつつ、Ruby 風の文法を持っているらしい。
ともかく↓このページの最初だけをやってみた。

 cf. 1 Introduction – はじめに – Elixir

インストール

↓このページから、Windows 用のインストーラをダウンロードしてインストールする。

 cf. Installing Elixir – Elixir

上のページからダウンロードできるのは Web インストーラで、実行中に Erlang と Elixir をダウンロードするので結構時間がかかるけど、基本的には指示に従うだけでインストールできた。

^o^ > elixir -v
Erlang/OTP 19 [erts-8.0] [64-bit] [smp:4:4] [async-threads:10]

Elixir 1.3.4

インタラクティブモード

iex コマンドで起動する。

^o^ > iex
Eshell V8.0  (abort with ^G)
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 40 + 2
42
iex(2)> "Hello" <> " world"
"Hello world"

<> は文字列連結の演算子。

スクリプトを動かす

elixir コマンドでスクリプトを実行することができる。スクリプトファイルの拡張子は exs。

IO.puts "Hello world
from Elixir"
^o^ > elixir hello.exs
Hello world
from Elixir

文字列の途中に自然に改行を入れられるのが新鮮。

今日はここまで。

PythonでMD5ダイジェスト値を求める

hashlib を使う。
試しに、MD5 を計算/チェックするスクリプトを書いてみた。

#!/usr/bin/env python
# encoding: utf-8

import argparse
import hashlib

script_version = "0.1.0"

def calc_md5(filename):
    m = hashlib.md5()
    f = open(filename, "rb")
    m.update(f.read())
    f.close
    return m.hexdigest()

def check_md5(filename, md5):
    return md5 == calc_md5(filename)

parser = argparse.ArgumentParser(description="calc/check MD5.")
parser.add_argument("file", metavar="FILE", action="store",
                    help="specify file.")
parser.add_argument("-v", "--version", action="version", version=script_version,
                    help="show version and exit")
parser.add_argument("-c", "--check", dest="check", action="store_true",
                    help="check MD5")
args = parser.parse_args()

if args.check:
    f = open(args.file, "r")
    s = f.read().strip()
    f.close()
    md5, filename = s.split(" ")
    if check_md5(filename, md5):
        print filename + " OK"
    else:
        print filename + " FAILED"
    else:
        filename = args.file
print calc_md5(filename) + " " + filename

実行例:

takatoh@apostrophe $ python md5.py sample.zip > a.md5
takatoh@apostrophe $ cat a.md5
72f163b717dbe53cd58902713dc10152  sample.zip
takatoh@apostrophe $ python md5.py -c a.md5
sample.zip OK