無限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.catydividable 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 taggedclass Inc(Command):
def execute(self, input):
return input + 1class 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