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

行番号を付加する関数的プログラム (3)

前回は、一意型を使って行番号を付加するプログラムを作成しました。前回のプログラムは、命令的なプログラムとほとんど変わらないものでした。今回は、一意型を使ったプログラムを関数プログラミングの観点からのメリットを意識したプログラムに変換します。
モナドのデメリットを説明した際に、プログラミングスタイル上の問題を指摘しました。そこで、一意型を用いることで、zipWithL()関数を用いた次のスタイルでプログラムの処理を記述する方法について検討します。

    lines = zipWithL(lambda num, line: str(num) + " " + line,
                     integers(), lines)

まず、必要な補助関数を作成します。最初は、ペアから要素を取り出す関数です。

def fst (t):
    f,s = get(t)
    return f
def snd (t):
    f,s = get(t)
    return s

次に、integers()関数とfromUnique()関数を再定義します。fromUnique()関数は、前のバージョンがThunkを扱うのに不十分であった部分の修正です。

def integers ():
    def f (n): return n, Thunk(lambda: f(n+1))
    return Thunk(lambda: f(1))

def fromUnique (x):
    x = get(x) # modified
    if isinstance(x, Unique):
        return get(x.__get__())
    else:
        raise UniqueError()

zipWithL()は以前作成したものをそのまま利用します。入出力関数なども前回前々回のものをそのまま利用します。
次に、それらを利用して、ファイルの内容をすべて読み込むfreadlines()関数を作成します。この関数は、ファイルの内容を一行ずつ読み込んで、遅延リストを作成して返します。必要以上に評価を進めないように、注意深くThunkを挿入します。

def freadlines (f0):
    def fun ():
        line, f = freadline(f0)
        if line:
            lines, f = freadlines(f)
            return (line, lines), f
        else:
            return None, f
    next = Thunk(fun)
    return Thunk(lambda: fst(next)), Thunk(lambda: snd(next))

次に、遅延リストを受け取ってファイルに出力するfwritelines()関数を作成します。

def fwritelines (lines, f):
    lines = get(lines)
    while lines:
        line, lines = lines
        f = fwrites(line, f)
        lines = get(lines)
    return f

プログラムのメイン処理は次のようになります。

def main (w):
    ifname = sys.argv[1]
    ofname = sys.argv[2]

    ifile, w = fopen(ifname, 'r', w)
    ofile, w = fopen(ofname, 'w', w)

    # modified
    lines, ifile = freadlines(ifile)
    lines = zipWithL(lambda num, line: str(num) + " " + line,
                     integers(), lines)
    ofile = fwritelines(lines, ofile)

    w = fclose(ofile, w)
    w = fclose(ifile, w)
    return w

一行ずつ交互に入力と出力を行っていることを確認するために、freadline()とfwrites()を次のように変更して、実行してみます。

def fwrites (text, f):
    print 'fwrites'
    _f = fromUnique(f)
    _f.write(text)
    return Unique(_f)

def freadline (f):
    print 'freadline'
    _f = fromUnique(f)
    text = _f.readline()
    return text, Unique(_f)

実行結果は次のようになります。

$ python prog20.py in.txt out.txt
freadline
fwrites
freadline
fwrites
freadline
fwrites
freadline

交互に入出力が行われていることを見て取ることができます。