このブログは、旧・はてなダイアリー「檜山正幸のキマイラ飼育記 メモ編」(http://d.hatena.ne.jp/m-hiyama-memo/)のデータを移行・保存したものであり、今後(2019年1月以降)更新の予定はありません。

今後の更新は、新しいブログ http://m-hiyama-memo.hatenablog.com/ で行います。

無限FizzBuzzをCatyスクリプトで書いてみる

CatyScript 2.0 では、メモリ資源(スタック)を消費しない再帰呼び出しを使って、意図的に無限ループを書ける。長時間セルフテスト(マゾテスト)のために無限ループは必要なのだ。

で、無限に続くFizzBuzzを書いてみる。実際には整数がラップアラウンドしてしまうが、エラーが起きないかぎり無限に続く。

事前定義されているコマンド:


/** 入力を1増やして出力する */
command inc :: integer -> integer;

/** 入力第1項目を第2項目で割り算して、商と余りを出力 */
command div :: [integer, integer] -> [integer, integer];

/** passと同じだが、引数で指定された秒数だけ何もしないで待つ */
command wait [number] :: T -> T;

無限FizzBuzz:真偽値はTrue, Falseを使うことにしてみる。eqはTrue, False対応と仮定。代入式は '>' で書く。


/** 入力が、引数で指定された数で割り切れるかどうかを判定する */
command dividable [integer] :: integer -> @(True|False) integer {
[pass > n, %1] | div | [nth 2, 0] | eq |
when {
True => @True %n,
False => @False %n
}
};

/** 入力値を開始の数として、FizzBuzzを永遠に続ける。
* 出力を生成することはないので、出力の型はneverである。
*/
command fizzbuzz-forever :: integer -> never {
dividable 15 |
when {
True => [pass, ("Fizz Buzz" | cout)] | nth 1, // 15の倍数
False => dividable 3 |
when {
True => [pass, ("Fizz" | cout)] | nth 1, // 3の倍数
False => dividable 5 |
when {
True => [pass, ("Buzz" | cout)] | nth 1,// 5の倍数
False => [pass, (cout)]| nth 1 // その他
}
}
} | wait 1 | inc | repeat
};

副作用を記述する構文マクロsideがあると少し短くスッキリ書ける。


command fizzbuzz-forever :: integer -> never {
dividable 15 |
when {
True => side {"Fizz Buzz" | cout}, // 15の倍数
False => dividable 3 |
when {
True => side {"Fizz" | cout}, // 3の倍数
False => dividable 5 |
when {
True => side {"Buzz" | cout},// 5の倍数
False => side {cout} // その他
}
}
} | wait 1 | inc | repeat
};

1回分なら、今のCatyでも書ける。


caty:misc> 10 | fizzbuzz-once.caty
Buzz
11
caty:misc> 10 | fizzbuzz-once.caty | fizzbuzz-once.caty
Buzz
11
12
caty:misc> 10 | fizzbuzz-once.caty | fizzbuzz-once.caty | fizzbuzz-once.caty
Buzz
11
Fizz
13
caty:misc> 14 | fizzbuzz-once.caty | fizzbuzz-once.caty |
> fizzbuzz-once.caty | fizzbuzz-once.caty | fizzbuzz-once.caty | void
14
Fizz Buzz
16
17
Fizz
caty:misc>

今、coutに不具合(仕様ともいえる)があって、一箇所sayに置き換えた。


// -*- coding: utf-8 -*-
// tihs is fizzbuzz-once.caty

dividable 15 |
when {
True => [pass, ("Fizz Buzz" | cout)] | nth 1, // 15の倍数
False => dividable 3 |
when {
True => [pass, ("Fizz" | cout)] | nth 1, // 3の倍数
False => dividable 5 |
when {
True => [pass, ("Buzz" | cout)] | nth 1,// 5の倍数
False => [pass, say] | nth 1 // その他
}
}
} | inc

dividableとinc、それとsayは、Pythonで書いておく。


# -*- coding: utf-8 -*-
#
#
from caty.command import Command
from caty.jsontools import tagged

class Inc(Command):
def execute(self, input):
return input + 1

class Dividable(Command):
def setup(self, n):
self.n = n
def execute(self, input):
r = input % self.n
if r == 0:
return tagged('True', input)
else:
return tagged('False', input)

# coutの代替
class Say(Command):
def execute(self, input):
t = type(input)
if (t is int) or (t is float):
print "%d" % input
else: # t is unicode
print "%s" % input
return None

スキーマ


module public; // -*- coding: utf-8 -*-

/** 入力を1増やして出力する */
command inc :: integer -> integer
refers python:cmd.Inc;

/** 入力が、引数で指定された数で割り切れるかどうかを判定する */
command dividable [integer] :: integer -> (@True integer | @False integer)
refers python:cmd.Dividable;

/** coutの代替 */
command say :: number | string -> void
refers python:cmd.Say;

// End of Module