パターンマッチング

 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。

^o^ > elixir hello.exs
Hello world
from Elixir

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

今日はここまで。

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

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

実行例:

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

2次元のリストの指定した座標を含むブロックの座標を列挙する

タイトルがわかりにくいな。
ホントは図を描けばいいのかもしれないけど、まず、2次元のリストを考える。で、そのリストの中に3×3のブロックが並んでいると考えてほしい。今日やりたいのは、ある座標(インデックス、0始まり、2次元なので当然2つの整数)を与えたときに、その座標を含むブロックの全座標を列挙したいってこと。
最初、どうやったらいいか悩んだけど、出来てみれば案外簡単だった。

実行例:

takatoh@apostrophe $ runhaskell block.hs 4 2
[(3,0),(3,1),(3,2),(4,0),(4,1),(4,2),(5,0),(5,1),(5,2)]
takatoh@apostrophe $ runhaskell block.hs 1 5
[(0,3),(0,4),(0,5),(1,3),(1,4),(1,5),(2,3),(2,4),(2,5)]

こんな感じ。

bottleアプリをuWSGIで動かす

やってみよう!

今回動かすアプリ

単純に、指定されたファイルをダウンロードさせるだけのアプリを書いた。

run 関数が直接 python index.py したとき用の記述で、application = default_app() が uWSGI 用の記述。

下準備

uWSGI で動かす前に、実行用の環境を env という名前で作っておく。

takatoh@apostrophe $ ls
index.py  requirements.txt
takatoh@apostrophe $ virtualenv env
New python executable in env/bin/python
Installing setuptools, pip, wheel...done.
takatoh@apostrophe $ source env/bin/activate
(env)takatoh@apostrophe $ pip install -r requirements.txt
Collecting bottle==0.12.10 (from -r requirements.txt (line 1))
/home/takatoh/w/myapp/env/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
  Using cached bottle-0.12.10-py2-none-any.whl
Installing collected packages: bottle
Successfully installed bottle-0.12.10
/home/takatoh/w/myapp/env/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
You are using pip version 7.1.2, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
(env)takatoh@apostrophe $ deactivate

それから、storage という名前のディレクトリを作って、ファイル(101_ObjectCategories.tar.gz)を入れておく。これをダウンロードしてみようってわけだ。

uWSGIで起動

まずは、コマンドラインから起動してみる。

takatoh@apostrophe $ uwsgi --http :8080 --wsgi-file index.py -H env

ダウンロードできるか、テスト。

takatoh@apostrophe $ wget http://localhost:8080/101_ObjectCategories.tar.gz
--2016-11-24 20:33:35--  http://localhost:8080/101_ObjectCategories.tar.gz
localhost (localhost) をDNSに問いあわせています... 127.0.0.1
localhost (localhost)|127.0.0.1|:8080 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 131740031 (126M) [application/x-tar]
101_ObjectCategories.tar.gz' に保存中

100%[======================================>] 131,740,031 68.1MB/s   時間 1.8s 

2016-11-24 20:33:37 (68.1 MB/s) - 101_ObjectCategories.tar.gz' へ保存完了 [131740031/131740031]

出来た。以前、Flask アプリを動かした時より簡単だな。

設定ファイルで起動

uWSGI 用の設定ファイル。

起動。

takatoh@apostrophe $ uwsgi uwsgi.ini
[uWSGI] getting INI configuration from uwsgi.ini

ダウンロードのテスト。

takatoh@apostrophe $ wget http://localhost:8080/101_ObjectCategories.tar.gz
--2016-11-25 08:35:08--  http://localhost:8080/101_ObjectCategories.tar.gz
localhost (localhost) をDNSに問いあわせています... 127.0.0.1
localhost (localhost)|127.0.0.1|:8080 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 131740031 (126M) [application/x-tar]
101_ObjectCategories.tar.gz' に保存中

100%[======================================>] 131,740,031 28.6MB/s   時間 4.5s 

2016-11-25 08:35:13 (27.8 MB/s) - 101_ObjectCategories.tar.gz' へ保存完了 [131740031/131740031]

OK。うまくいった!

Windows 10環境のrubygemsでSSLエラーが発生した場合の対処法

新しい PC のセットアップ中、rubygems でエラーが発生した。環境は次の通り。

  • Windows 10 Home
  • Ruby 2.3.1 (RubyInstaller)
  • gem 2.5.1

発生したエラーは:

^o^ > gem install acm
ERROR:  Could not find a valid gem 'acm' (>= 0), here is why:
          Unable to download data from https://rubygems.org/ - SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (https://api.rubygems.org/specs.4.8.gz)

どうも、SSLのエラーらしい。

で、いろいろとググりながら試した結果、次の手順でうまく回避できたのでメモしておく。

rubygemsのアップデート

普通なら gem コマンドでアップデートできるけど、それが使えないので rubygems.org からダウンロードしてアップデートする。
 cf. rubygems-update 2.6.8
上のリンクから、rubygems-update の最新版 2.6.8 をダウンロード、インストールする。

^o^ > gem install rubygems-update-2.6.8.gem --local
Successfully installed rubygems-update-2.6.8
Parsing documentation for rubygems-update-2.6.8
Installing ri documentation for rubygems-update-2.6.8
Done installing documentation for rubygems-update after 39 seconds
1 gem installed

^o^ > gem -v
2.5.1

インストールしただけではアップデートされないので、update_rubygems コマンドでアップデートする。

^o^ > update_rubygems
RubyGems 2.6.8 installed
Parsing documentation for rubygems-2.6.8
Installing ri documentation for rubygems-2.6.8
(以下略)

長々とメッセージが出るけど、これでアップデートできた。

^o^ > gem -v
2.6.8

確認

これで、正常に使えるようになったはず。

^o^ > gem install acm
Fetching: thor-0.19.1.gem (100%)
Successfully installed thor-0.19.1
Fetching: acm-0.4.2.gem (100%)
Successfully installed acm-0.4.2
Parsing documentation for thor-0.19.1
Installing ri documentation for thor-0.19.1
Parsing documentation for acm-0.4.2
Installing ri documentation for acm-0.4.2
Done installing documentation for thor, acm after 2 seconds
2 gems installed

OK。

参考ページ

 cf. Ruby distros, RubyGems, and SSL—Oy vey!

新しいPCが届いた

HP の EliteBook Folio G1 ってやつ。1週間ほど前の夜中、薄くて軽くて頑丈、っていう惹句についうっかりとポチッたもの。確かに12.5型にしては軽い。
OS は Ubuntu じゃなくてプリインストールの Windows 10 Home をそのまま使うことにした。MS-Office を使いたいからそのほうがいいんだよね。
朝からセットアップを始めて、一通り終わったところ。rubygems がエラーをはいてちょっとハマったけど、他は特に問題なし。ホスト名は sofa にした。
以下、インストールしたアプリケーション。

  • Microsoft Office 2013
  • Firefox
  • Dropbox
  • Ruby
  • Python
  • Node.js
  • Gauche
  • Haskell
  • Perl
  • Git
  • 秀丸エディタ

とりあえずこんなところか。rubygems の問題の解決方法はエントリーを分けて書く。

辞書の値によってキーを削除する

例えば、こんな辞書があったとする。

>>> dic = {'Andy': 27, 'Bill': 32, 'Charlie': 24}

この辞書から、値が 30 を超えるキー(つまり ‘Bill’)を削除したい。こうすると:

>>> for k in dic:
...     if dic[k] > 30:
...         del(dic[k])
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

エラーになる(イテレーションの最中に辞書のサイズが変わってるって怒られる)。
でもキーは削除されてる。

>>> dic
{'Charlie': 24, 'Andy': 27}

なんでだかは解らない。
エラーが出ないようにするには items() メソッドを使えばいい。今度は値が 25 を超えるキーを削除してみる。

>>> for k, v in dic.items():
...     if v > 25:
...         del(dic[k])
...
>>> dic
{'Charlie': 24}

無事に消えた。