/home/matstani/weblog

programming log.

[Clojure] ちょっと難しいシーケンス処理

Sequence Abstractions in Clojure

上記のリンクはClojureのシーケンスに関する記事なのですが、”Difficult Sequences”のところが面白かったのでメモします。

Clojureでは、mapfilterを始めとした豊富なシーケンス用関数がありますが、例えば「1つ前の要素を参照する」処理はどう書くのでしょうか。
手続き型言語では、以下のように記述できます。

1
2
3
4
v = [1 2 3 4 5];
for (i=1; i<v.length; i++)
    print v[i] + v[i-1];
-> 3 5 7 9

この「1つ前の要素を参照する」処理をシーケンスで実現するために、まず「1つずらしたシーケンス」を考えてみます。

1
2
  [1 2 3 4 5]
[1 2 3 4 5]

すると、上下が重なっている部分が、「1つ前の要素」と「現在の要素」の組み合わせになっています。
Clojureのmap関数では、復数のシーケンスを同時に扱うことができます。

1
2
(map + [1 3] [2 4])
-> (3 7)

渡したシーケンスの最初の要素同士、2番目の要素同士、がそれぞれ”+”されています。
「元のシーケンス」、「1つずらしたシーケンス」、を用意してmap関数に渡してやれば、目的の処理が実現できそうです。
「1つずらしたシーケンス」を得るには、rest関数が利用できます。

1
2
3
(def v [1 2 3 4 5])
(rest v)
-> (2 3 4 5)

rest関数が返すのは、シーケンスの先頭以外を含むシーケンスです。
これらを組み合わせると目的の処理を実現できます。

1
2
(map + v (rest v))
-> (3 5 7 9)

ここでは

1
2
v        -> (1 2 3 4 5)
(rest v) -> (2 3 4 5)

上記2つのシーケンスの重なっている要素同士の足し算”+”が行われ、目的が実現できています。
map関数に長さの異なるシーケンスを渡された場合、長いシーケンスの余り部分は無視されます。

Comments