関数プログラミングのアプローチ (25)

配列

配列は、プログラミングにおいて、もっとも基本的なデータ構造です。しかし、関数プログラミングにおいて、配列の取り扱いは問題を孕んでいます。今回は、関数プログラミングにおける配列の問題について考える前に、配列の特徴について確認します。
配列の特徴は次のようにまとめられます。

  • サイズが可変長で初期化時に決まる
  • キーが0から始まる連続的な整数
  • メモリ上での表現が非常にコンパクト
  • 参照と更新が定数時間で非常に効率的

このような特徴のため、プログラミングにおいて配列は、さまざまな重要なアルゴリズムで利用されています。ここでは例として2つのアルゴリズムを取り上げます。

ハッシュテーブル

ハッシュテーブルは、シンボルテーブルの実装アルゴリズムの1つで、定数時間の参照・更新を実現します。これは、バランス木による実装よりも効率的であるため、特別な理由がない場合はシンボルテーブルはハッシュテーブルとして実装することが普通です。
ハッシュテーブルは、内部で配列を利用します。

table = [None] * TABLESIZE

そして、要素にアクセスするには、キーのハッシュ値を取得して、それをインデックスとして配列にアクセスします。配列の値のキーが等しければそれが求める要素で、違うならば配列の次の要素を順に確認します。

def get (key):
    idx = hash(key) % len(table)
    while table[idx]:
        if key == getKey(table[idx]):
            return getValue(table[idx])
        else:
            idx = (idx + 1) % len(table)

更新の場合も、同様の方法で対応する配列のインデックスを取得し、新しい値で配列の値を書き換えます。

ダイクストラ

ダイクストラ法は、グラフ中の2つのノードをつなぐ最短のパスを求めるの1つです。この手法は動的計画法(Dynamic Programming)の1種で、計算中の状態を記録する配列を用意し、計算が進むに従って配列を書き換えていきます。
ダイクストラ法では、各ノードについての開始ノードからの現在の距離を記録する配列と、各ノードの直前のノードを記録する配列と、計算が進行中のノードを管理するプライオリティキューを利用します。

def shortestPath (graph, s, t):
    wt = [MAXNUM] * graph.size
    spt = [-1] * graph.size
    pq = PriorityQueue(graph.size)

    pq.insert(graph.node(s))
    while pq.empty():
        v = pq.getMin()
        if v.idx == t:
            ls = [t]
            while spt[t] >= 0:
                t = spt[t]
                ls.insert(0, t)
            return ls

        for e in graph.adjacent(v.idx):
            if wt[e.idx] > wt[v.idx] + 1:
                wt[e.idx] = wt[v.idx] + 1
                if spt[e.idx] < 0:
                    pq.insert(e)
                else:
                    pq.update(e)
                spt[e.idx] = v.idx

    return None

さらに、プライオリティキューの代表的なアルゴリズムにヒープがありますが、これも配列を用います。ヒープは2分木の1種ですが、木を配列とインデックスの値を用いて表現します。

pq = [None] * HEAPSIZE
lastindex

def insert (e):
    lastindex = lastindex + 1
    pq[lastindex] = e
    idx = lastindex
    while idx > 1:
        idx2 = idx / 2
        if pq[idx2] > pq[idx]:
            t = pq[idx2]
            pq[idx2] = pq[idx]
            pq[idx] = t
            idx = idx2
        else:
            break

このように、配列はさまざまなアルゴリズムで基本的なデータ構造として利用されています。