関数型プログラミング言語でLisp方言の1つのClojureを触ってみました。(『7つの言語 7つの世界』第7章)

7つの言語 7つの世界』(オーム社)の第7章で紹介されているClojureです。
Clojure
http://clojure.org/

1. Clojureについて

  Clojureは関数型言語Lisp方言の1つです。Lipsは非常に歴史が古い言語であるため、ClojureのほかにEmacs Lisp、Common Lisp、Schemeなど多くの方言をもっています。Lispがもつ高い柔軟性とJavaとの統合性の高さから(あと括弧が他の方言と比べると少ないらしい)、多々あるLisp方言のなかでもっとも成功しているとされるClojureを『7つの言語 7つの世界』は選択しています。
 ここではClojureの紹介をしていくつもりですが、私にとって初めてさわる「Lisp」であり、Lispとしての特徴とClojureとしての特徴の区別がほとんどつかないため、Lispの特徴とClojureの特徴が混在した紹介にならざるを得ませんでした。ご了承ください。
 なお、他の言語もそうですが、Clojureでは 『7つの言語 7つの世界』だけではなく、Web上の掲載されている様々な情報を参考にさせていただきました。

2. Clojureのインストール

 Web上にはいろいろなインストール方法が紹介されていましたが、MacOSX Lionへのインストールの場合、HomeBrewでLeiningenをインストールすれば必要なものがもろもろインストールされて一発でした。

$ brew install leiningen

3. Hello,World

 起動と終了は以下の通り。終了方法は最初にそれを知った時に「なるほど、そうか!」という気分になりますね。
・起動

$ lein repl
REPL started; server listening on localhost port ○○○○○
user=>

 
・終了

user=> (exit)
$

 
文字列はダブルクォートで囲います。

user=> (println "Hello,World")
Hello,World
nil

 
 制御構文はこんな感じ。

user=> (if (> 2 1) (println "2"))
2
nil

 
演算

user=> (+ 1 1)
2
user=> (- 2 1)
1
user=> (+ 1 2 3)
6
user=> (* (+ 1 1)(- 5 1))
8

上はそれぞれ

  • (+ 1 1) → 1+1
  • (- 2 1) → 2-1
  • (+ 1 2 3) → 1+2+3
  • (* (+ 1 1)(- 5 1)) → (1+1)*(5-1)
  • をあらわしています。

4. リスト

Lispではリストは以下のように丸括弧( )で括弧って要素を空白で区切って記述します。
(要素 要素 要素 ・・・)
 つまり・・
 (1 2 3)
というデータセットも
 (println “2”)
というコードも同じ記述方法で記述します。Lispはあらゆるものがリストだと言われ、データ以外のコードも必要に応じてリストとして処理することができます。これがLispが強力なメタプログラミング機能を持つ理由の1つであるようです。
 コードの記述はリストの最初の要素として関数や演算子などを記述する前置記法と呼ばれる記述方法で記述します。残りの要素は引数です。
 (関数 引数 引数・・) 
   ex. (println “2”)
 (演算子 引数 引数・・・)
   ex. (+ 1 1)
 データのセットとしてリストを記述したい場合に
 (1 2 3)
とそのまま記述してしまうと最初の要素が関数と評価されてエラーになってしまいます。
 
 list関数を使用するか、括弧の前にアポストロフィ(‘)を置きます。

user=> (list 1 2 3)
(1 2 3)
user=> '(1 2 3)
(1 2 3)

 ちなみに式とコードと混同しやすいため、Clojureでは慣習的に式はリストで、データのセットは後述するペクタを使用するそうです。
 

5. ベクタ・セット・マップ

 
 ペクタ(vector。「ベクトル」と呼ぶ人も)という角括弧[ ]で記述するデータセットです。また、角括弧を使用せずとも、リストでvector関数を使用してもベクタを生成できます。

user=> [1,2,3]
[1 2 3]
user=> (vector 1 2 3)
[1 2 3]

 慣習的な使い分けはともかく、その機能を見る限り、データセットとして使用する場合のリストとベクタの違いがよくわからなかったのですが、内部構造でのデータの持ち方が異なるようです。
 リストとベクタは要素の順序が重要な意味を持ちますが、意味を持たないセットというデータセットもあります。

user=> #{1 2 3}
#{1 2 3}

 
  キーと値を対応づけるRubyでいうハッシュに相当するデータセットがマップです。上のセットというデータセットと同じ{ }で囲いますが、{キー 値 キー 値}という順序で並んでいます。キーはコロンをつけたシンボルが使用されます。

user=> {:a "a" :b "b"}
{:a "a", :b "b"}
user=> {:a 1 :b 1}
{:a 1, :b 1}

  読みづらいということで、Clojureではキーと値ごとに区切り記号としてカンマをいれることも可能です。

user=> {:a "a", :b "b"}
{:a "a", :b "b"}
user=> {:a 1, :b 1}
{:a 1, :b 1}

 

6.シーケンス

シーケンスとは・・・
 とかけるほどシーケンスのことをよく理解していませんが、リスト、ベクタ、セット、マップ、文字列、ファイルシステム構造をラップして共通のインターフェイスを提供する抽象化されたなにか(型?)のようです。共通の関数が使える、ということですかね。 

user=> (count (list 1 2 3))
3
user=> (count [1 2 3])
3
user=> (count #{1 2 3})
3
user=> (count "abc")
3

 

7.変数

 Clojureでの変数の定義以下を読むとdef関数を使用するようです。
Clojure – vars

user=> (def a 1)
#'user/a
user=> a
1

一度変数を定義しても変更可能な(mutable)ようです。

user=> (def a 2)
#'user/a
user=> a
2

  ソフトウェアトランザクショナルメモリを使用して同じ変更可能な場所をより安全に参照するためにデータをラップするref関数というものもあるようです。

user=>  (ref "Totoro")
#<ref@1726c5a5: "Totoro">

  
ref関数でラップした値をdef関数で変数とひもづけることで参照することができるようになります。

user=>  (def movie (ref "Totoro"))
#'user/movie
user=> @movie
"Totoro"
user=> (deref movie)
"Totoro"

 参照するときは変数の頭に@をつけるか、deref関数を使用します。
参考: Clojure – refs
 ref関数でラップしたデータは同じトランザクションのスコープ内でしか変更することができませんが、そのスコープ外でデータをラップするためにアトムといものがClojureにはあります。

user=>  (atom "Totoro")
#<atom@762d80ae: "Totoro">
user=>  (def movie (atom "Totoro"))
#'user/movie
user=> movie
#<atom@54b4b0a4: "Totoro">
user=> @movie
"Totoro"

8.関数

 defn関数で関数を定義することができます

user=> (defn hello [x] (str "Hello," x))
#'user/hello
user=> (hello "world")
"Hello,world"

 
 defn関数は引数をうけないとエラーを検出していまうようですが、引数を受け取らない関数を定義する場合は、fn関数では無名関数をつくり、それをdef関数で変数に代入?する方法があるようです。

user=> (def hello_world (fn [] (println "Hello,World")))
#'user/hello_world
user=> (hello_world)
Hello,World
nil

参考
プログラミングClojure 勉強メモ 第1章 Getting Started – Life is a Role Playing Game
関数 – Closer to Clojure
untitled: 『プログラミング Clojure』 で勉強 第2章 – 2

関数型プログラミング言語Erlang を触ってみました。(『7つの言語 7つの世界』第6章)

7つの言語 7つの世界』(オーム社)の第6章で紹介されているErlangです。
Erlang
http://www.erlang.org/
 
 この第6章のErlangからこの本では、第7章のClojure、第8章のHaskellと関数型言語が続きます。先のエントリで紹介した第5章のScalaもあわせると7つのうち、関数型言語に4つも割かれています。著者のの関数型言語への関心の高さと関数型言語に対する注目の高さが伺えます。

1.Erlangについて

Erlang(アーラン)はEricsson Languageの略で、その名前にあるとおり、Ericsson社が開発した関数型言語です。Prologをベースに開発されました。耐障害性と並行性が大きな売りになっているようで、Erlangの「プロセス」が大きくそれに貢献しているようです。
プロセス / Erlang World
 ErlangのMacOS X LionへのインストールはHomebrewを使用しました。2012年6月12日時点でバージョンはR15B01です。

$ brew install erlang

 

2.Hello,World

 ターミナルで「erl」と打つとErlangのシェルが起動します。

$erl
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1  (abort with ^G)
1>

 ちなみに終了は「 q().」。最初、これが分からず難儀しました。

1> q().
ok
$

  
基本的な書き方ですが、行末は必ずピリオド(.)をつける。コメントはパーセント記号(%)(行末のピリオドは不要)

2>%数値
2> 1+1.
2
3>%文字列
3> "Hello,World".
"Hello,World"
4>%数値と文字列を加算してみると・・
4> 1+"1".
** exception error: bad argument in an arithmetic expression
     in operator  +/2
        called as 1 + "1"

 

3.変数とアトム(シンボル)

 
 変数は大文字で始まる文字列で定義します。小文字で始まる文字列は後述する「アトム」というシンボルを表すため、変数として使えません。

5> A = 1.
1
6> a = 1.
** exception error: no match of right hand side value 1

 
 Scalaは変更不可能な変数と変更可能な変数を選ぶことができましたが、Erlangでは変更不可能な変数しか定義することができません。

7> A = 2.
** exception error: no match of right hand side value 2

 小文字で始まる文字列で「アトム」というシンボルを表します。Rubyでいうところのコロン(:)をつけた例えば”:name”、”:price”みたいなものです。

8> hello_world.
hello_world

 
 シングルクォーテーション(’)で囲うと大文字から始まる文字列もアトムとして扱われるようです。シングルクォーテーション(’)とダブルクオーテーション(“)のどちらで囲っても文字列として扱われるRubyに慣れていると少し戸惑います。

9>'Hello_world'.
'Hello_world'

4. リストとタプル

 Erlangには、Prologと同じようにリストとタプルという配列が用意されています。リストは角括弧[ ]で囲われた可変長の配列で、タプルは中括弧{ }で囲われた固定長の配列です。Prologではタプルを()で囲うところが違いますね。

10>%リスト
10>[1,2,3,4,5]
[1,2,3,4,5]

 Erlangは文字列を実はリストとして扱っています。そのため、以下のような整数のリストから文字列を返してくる。

11> [72,101,108,108,111,44,87,111,114,108,100].
"Hello,World"

 ちなみに上の数字はそれぞれASCIIコードです。日本語対応していない処理系を使用すると英数字以外を入力するとそのままリストで返されてしまいます。以下はユニコード表の数字ですね。

12> "ドラゴンボール".
[12489,12521,12468,12531,12508,12540,12523]

 

4. タプルとパターンマッチ

 ErlangにはRubyでいうハッシュのようなものがないようですが、タプルで以下のように書くことができます。book、title、author、publisherはアトムでハッシュでいうところのキーとして使用しています。

13> {book,{title,"Dragon Ball"},{author,"Toriyama Akira"},{publisher,"Syueisya"}}.
{book,{title,"Dragon Ball"},
      {author,"Toriyama Akira"},
      {publisher,"Syueisya"}}

上のタプルをBookという変数に代入します。

14>  Book = {book,{title,"Dragon Ball"},{author,"Toriyama Akira"},{publisher,"Syueisya"}}.
{book,{title,"Dragon Ball"},
      {author,"Toriyama Akira"},
      {publisher,"Syueisya"}}

今度は上と同じ構造で”Dragon Ball”等の文字列を入れていたところを変数に置き換えたタプルに上のタプルを変数Bookを経由して代入させます。

15> {book,{title,Title},{author,Author},{publisher,Publisher}} = Book.
{book,{title,"Dragon Ball"},
      {author,"Toriyama Akira"},
      {publisher,"Syueisya"}}

 するとデータ構造でマッチさせることで、それぞれの変数にそれぞれの値が代入され、以下のように取り出すことができます。

16> Title.
"Dragon Ball"
17> Author.
* 1: variable 'Author' is unbound
18> Author.
* 1: variable 'Author' is unbound
19> Publisher.
"Syueisya"

5. 関数

テキストエディタで以下のように「test.erl」というerlという拡張子を持つファイルを作成します。

%-moduleでモジュール名(この場合はtest.erlの拡張子を取り除いたもの)を定義。
-module(test)
%関数名を定義(/1の1は引数の数)
-export([print/1])
%以下から関数を定義
print(A) -> A

Erlangシェルを起動して「test.erl」をコンパイルします。

1> c(test).
{ok,test}

  コンパイル後にtestモジュールのprint関数が利用できるようになります。

2> test:print("Hello World").
"Hello World"

 

5.1. 無名関数

funという関数で無名関数、つまり名前のない関数を定義できます。

3>fun(A) -> A end.
#Fun<erl_eval.6.82930912>

 
この無名関数を変数に代入できます。

4>Print = fun(A) -> A end.
#Fun<erl_eval.6.82930912>
5>Print("Hello, World").
"Hello, World"

 

5.2. 高階関数

 関数を引数として受け取ったり、戻り値として返す高階関数。関数を戻り値として返すというのはこういうことでしょうか。

6>HighPrint = fun(X) -> Print(X) end.
#Fun<erl_eval.6.82930912>

  関数を引数として受け取る。下ではまずTestというリストを作成し、リストの要素を順番にPrint関数に引数として渡しています。戻り値もそのままリストになってしまうので分かりづらいですが、”Hello”、”World”、”Erlang”という戻り値が返ってきてます。

7> Test = ["Hello","World","Erlang"].
["Hello","World","Erlang"]
8> lists:map(Print,Test).
["Hello","World","Erlang"]

ハイブリットなプログラミング言語 Scala を少し触ってみました(『7つの言語 7つの世界』第5章)

 『7つの言語 7つの世界』(オーム社)の第5章で紹介されているScalaです。
Scala
http://www.scala-lang.org/

1. 「はじめに」にもならない駄文

 いまさらの紹介で恐縮ですが、『7つの言語 7つの世界』は、1言語に1章を割いて7つの言語を紹介して、いろいろなプログラミング言語のパラダイムに触れてみることを目的とする書籍です。1章はおおよそ3節から4節で構成されており、お察しのとおり、数字が上がるごとに段々難易度が上がっていきます。この第5章のScalaの場合は
5.1 Scalaの概要
5.2 Scalaのオブジェクト指向言語機能について
5.3 Scalaの関数型言語機能について
5.4 並行処理、XML処理
※上は節名ではありません
 
 という構成になっています。5.2のオブジェクト指向言語機能まではまぁなんとか、5.3の関数言語言語では配列の説明を読んでいるかと思っていたけど、もしかして違う場所に迷い込んでしまったかという気分になり、その気分を引きずったままの並行処理の説明でしたので、肝心なところが理解できている気がしないのですが、書けるところまで書いてみます。

2.Scalaのインストール

今回は公式サイトからではなく、以下を参考にHomebrewでインストールしました。
Mac OS X 開発環境構築手順:Scalaインストール – Shinya’s Daily Report

3. Scalaについて

 Scalaはオブジェクト指向型言語と関数型言語の2つのパラダイムが融合したハイブリット型のプログラミング言語で、JVM上で動作し、Javaのライブラリが使用できるなど、Javaとの親和性が重視されています。変数も変更可能は(mutable)な変数と変更できない(immutable)な変数、どちらでも定義できるようになっています。Javaのような書き方もできるし、関数型言語的な書き方も可能になっているなど、少しずつ関数型言語のパラダイムに移行するということも可能です。
 Scalaは以下の3通りの実行方法が用意されています。

  1. Rubyにおけるirbのような対話形式
  2. インタプリタによる実行
  3. コンパイルして実行

 というわけで、私は基本的に1の対話形式でいろいろと試してみました。

4. Hello,World

 ”scala”とコマンドを打つとScalaのシェルが起動します。

$ scala
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_31).
Type in expressions to have them evaluated.
Type :help for more information.
scala>

 というわけで、まずはHello,World。

scala> println("Hello,World!!")
Hello,World!!

 まずは数値と文字列の扱いから。 

scala> 1+1
res5: Int = 2
scala> "1"+"1"
res6: java.lang.String = 11
scala> 1+"1"
res7: String = 11

このあたりはRubyと同じですかね。

4. オブジェクト指向言語機能

 
  Javaを知らないのでRuby学習者からの見た意見ですが、この書籍を読む限りはオブジェクト指向型言語に触れたことがある方なら、ここはさほどむずかしくはないのではないかと。私の場合は静的な型付けに慣れていないのでそこで戸惑うところですが、それでもまぁなんとか。ということで、ここはさらりと飛ばします。
 1点、おもしろいと感じたのは、Nothingというクラスの存在ですべてのクラスのサブクラスだそうです(Scalaのルートのクラス階層はAnyだそう)。Javaにもあるのかしら?

5. 関数型機能

<

h4>5.1 変数

 関数型言語は「副作用がない」といわれるように、同じ入力に対しては常に同じ値が返ってくる、他に影響を及びさないということが大きな特徴になっています。一度代入した変数の変更を認めないというものそれ故の特徴とされているようですが、Scalaは変更不能な変数もオブジェクト指向言語のような変更可能な変数もどちらも定義することが可能になっています。
変更可能な(mutable)変数を定義するときは var で定義する。

scala> var a = 1
a: Int = 1
scala> a = 2
a: Int = 2

変更不能な(immutable)変数を定義するときは valで定義する。一度定義したら変更はできない。

scala> val b = 1
b: Int = 1
scala> b = 2
<console>:8: error: reassignment to val
       b = 2
         ^

5.2 リスト・セット・マップ

リスト・・・、配列とどう違うのだろうと思いながら触っていました。

scala> List(1,2,3)
res0: List[Int] = List(1, 2, 3)
scala> List("a","b","c")
res1: List[java.lang.String] = List(a, b, c)
scala> List(1,2,"c")
res2: List[Any] = List(1, 2, c)

 正直、よくわかりません・・・・。この書籍にはリストを操作する関数も紹介されていましたが、ここでは省略。
  リストは順番が重要な意味を持っていますが、特に順番が意味をもたない「セット」というコレクションもあります。順番が意味をもたないというとキーと値をセットにしたハッシュを思い出しますが、それはそれでScalaにもありまして、後述する「マップ」というものがそれに該当します。

scala> Set("Taro","Jiro","Hanako")
res4: scala.collection.immutable.Set[java.lang.String] = Set(Taro, Jiro, Hanako)

 
 セットはキーを持たない、順番が意味を持たないコレクションということで、配列とハッシュの中間に位置するようなものなのでしょうか。でも、1つ1つの要素をどうやって取り出すのだろう?
 Rubyでいうハッシュに当たるのがマップです。Map関数を使用して定義しましたが、HashMap関数を使えば、キーと値の型も指定できるみたいですね。

scala> Map("Taro" -> "painting","Jiro" -> "photo","Hanako" -> "zen")
res5: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(Taro -> painting, Jiro -> photo, Hanako -> zen)

6. 並行処理

  並行性を理解するのが、Scala学習最大の肝ではないかということは感じたのですが、あまりここで書くほど理解にいたらず。というわけで、今回はパスします。ここは後日ぜひやり直したいところですね。