プログラマの最低限の能力を測る試験として Fizz Buzz という問題が知られている。

基本的には 1 から 100 までの数値を表示するのだが、

数値が 3 で割りきれるときは数値のかわりに Fizz と表示する

数値が 5 で割りきれるときは数値のかわりに Buzz と表示する

数値が 3 と 5 の両方で割りきれるとき (つまり 3 と 5 の最小公倍数である 15 で割りきれるとき) は数値のかわりに FizzBuzz と表示する

という規則を実現するプログラムを書くというものだ。

しばしば技巧的な要素を盛り込んで遊ぶ題材としても用いられる。

これを Common Lisp でなるべく高速に処理しようと試みている記事を先日読んだ。

Common Lispで最速のfizzbuzzを実装した話 - gos-k’s blog

要するに、実行するよりも前に文字列を組み立てておき、実行時には構築済みの文字列を表示するだけというメカニズムである。

私は、 Scheme (R6RS) で同様のことをしようとするとどうなるか考えてみた。

( import ( rnrs )) ( define-syntax make-fizzbuzz-string ( lambda ( ctx ) ( syntax-case ctx () (( _ n ) ( integer? ( syntax->datum #'n )) ( call-with-string-output-port ( lambda ( port ) ( do (( i 1 ( + i 1 ))) (( < ( syntax->datum #'n ) i )) ( display ( cond (( zero? ( mod i 15 )) "fizzbuzz" ) (( zero? ( mod i 5 )) "buzz" ) (( zero? ( mod i 3 )) "fizz" ) ( else i )) port ) ( newline port )))))))) ( define ( fizzbuzz ) ( display ( make-fizzbuzz-string 100 ))) ( fizzbuzz )

Guile で逆アセンブルするとこうなる。 (当初は Chez Scheme を使おうとしたのだが、逆アセンブル機能が提供されていなかった。)

0 (assert-nargs-ee/locals 0) ;; 0 args, 0 locals 2 (toplevel-ref 1) ;; #<procedure display (object #:optional port)> 4 (object-ref 2) ;; "1

2

fizz

4

buzz

fizz

7

8

fizz

buzz

11

fizz

13

14

fizzbuzz

16

17

fizz

19

buzz

fizz

22

23

fizz

buzz

26

fizz

28

29

fizzbuzz

31

32

fizz

34

buzz

fizz

37

38

fizz

buzz

41

fizz

43

44

fizzbuzz

46

47

fizz

49

buzz

fizz

52

53

fizz

buzz

56

fizz

58

59

fizzbuzz

61

62

fizz

64

buzz

fizz

67

68

fizz

buzz

71

fizz

73

74

fizzbuzz

76

77

fizz

79

buzz

fizz

82

83

fizz

buzz

86

fizz

88

89

fizzbuzz

91

92

fizz

94

buzz

fizz

97

98

fizz

buzz

" at /c/Users/saito/Documents/fast-fizzbuzz.scm:22:11 6 (tail-call 1) at /c/Users/saito/Documents/fast-fizzbuzz.scm:22:2

構築済みの文字列を表示するだけになっていることがわかる。

Document ID: e2741c0cea07aa2fbddc630e30240a15