言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

Lucid 言語

Lucid 言語というのを教えてもらいました。Lucid も 70 年代に作られた古い言語で、データフロー言語のハシリとされます。残念ながら私のパソコンで動く処理系は無いみたいだけど、Haskell に翻訳しながら試してみます。

まず最初の例は、数列の合計を求めます。

total
  where
     total = 0 fby total + x
  end;

Lucid では、変数は一つの値では無く、データの流れを表します。演算子の意味はこうです。

  • fby "followed by" あとに続くストリーム
  • + ストリームの足し算

果たして、このプログラムがどのように動くか想像出来ますでしょうか?普通、合計を求めるプログラムというのは引数に数列を与えるとズバッと解が返るようにするでしょう。しかし Lucid 言語はデータフロー言語なので、じわじわと数列を与えるとじわじわと答えが返って来るのです。と口で言っても動作が分からないと思うので Haskell で書くと以下の通り。

-- Lucid 対応便利関数
xs +++ ys = map (\(x, y) -> x + y) (zip xs ys)
xs /// ys = map (\(x, y) -> x / y) (zip xs ys)
run f = do contents <- getContents
           mapM (print.show) $ f $ map read $ lines contents

total x = 0 :total x +++ x
-- Main> run total -- "" の中身が出力です。
-- "0"
-- 1
-- "1"
-- 2
-- "3"
-- 3
-- "6"
-- 4
-- "10"
-- 5
-- "15"

次の例は平均を求める。これもじわじわと平均を求めます。

running_avg
  where 
     sum = first(input) fby sum + next(input);
     n = 1 fby n + 1;
     running_avg = sum / n;
  end;

これも Haskell 版です。

running_avg input = sum input /// n
    where sum input = head input : sum input +++ tail input
          n = 1 : n  +++ [1.0,1.0..]
-- Main> run running_avg
-- "1
-- 1.0"
-- 2
-- "1.5"
-- 3
-- "2.0"
-- 4
-- "2.5"
-- 5
-- "3.0"

Haskell では、ストリームという特別なデータ構造は無く、リストをそのままストリームとして使います。しかし演算子を自由に定義出来るので、工夫して出来るだけ Lucid に似せて定義する事が出来ます。例えば Lucid の fby は Haskell の : に、first は head に、next は tail にそれぞれ対応します。工夫するだけでこれだけそっくりになるとは驚きですね。