😜

Sleep FizzBuzz

2022/02/10に公開

Twitter で散歩していたら、昨日こんなツイートを見付けました。

https://twitter.com/m_ou_se/status/1491327611150102532

コードはこんな感じ。(ツイート主がメンションで修正している内容を反映済み)

main.rs
use std::{
    thread::{self, sleep},
    time::Duration,
};

fn main() {
    thread::spawn(|| {
        sleep(Duration::from_millis(2100));
        loop {
            print!("Fizz\x1b[K");
            sleep(Duration::from_secs(3));
        }
    });

    thread::spawn(|| {
        sleep(Duration::from_millis(4200));
        loop {
            print!("Buzz\x1b[K");
            sleep(Duration::from_secs(5));
        }
    });

    for i in 1.. {
        print!("\n{i}\r");
        sleep(Duration::from_secs(1));
    }
}

このコード、動かすとちゃんと FizzBuzz が表示されます。

コードが読める方であればおおよそ検討が付くはず。1秒間隔で数字を表示するループ、そしてスレッドで 3秒毎に Fizz を表示するループと、スレッドで 5秒毎に Buzz を表示するループで構成されます。毎秒数字を表示する部分は \n{i}\r なので数字を表示した直後にカーソルを行の先頭に戻します。
このコードのミソは3秒毎に Fizz を表示するループには初期ウエイトとして 2100 ミリ秒、5秒毎に Buzz を表示するループには初期ウエイトとして 4200 ミリ秒が与えられています。これにより、Fizz を表示するループでは毎秒表示される数字の 3 の倍数秒の時、数字が表示された 100 ミリ秒後に Fizz で上書きます。また 5 の倍数秒の時、数字が表示された 200 ミリ秒後に Buzz で上書きます。ウエイトは Fizz のスレッドよりも Buzz のスレッドの方が1秒単位で言うと 100 ミリ秒多いので、15の倍数秒の時は Fizz と Buzz が連続で表示されます。

これにより FizzBuzz が動くという仕組みです。

おもしろいですね。コード面接で FizzBuzz 書いてと言われた時にこんなコードを書いたら驚かれるんじゃないでしょうか。(FizzBuzz をコード面接で書かせる所なんて無いと思いますが)

追記

ちなみに Go で書くとこんな感じ

main.go
package main

import (
	"fmt"
	"time"

	"github.com/mattn/go-colorable"
)

func main() {
	t1 := time.NewTicker(1)
	t3 := time.NewTicker(1)
	t5 := time.NewTicker(1)
	t1.Stop()
	t3.Stop()
	t5.Stop()

	t1.Reset(time.Second)
	time.Sleep(time.Millisecond * 5)
	t3.Reset(time.Second * 3)
	time.Sleep(time.Millisecond * 5)
	t5.Reset(time.Second * 5)

	out := colorable.NewColorableStdout()
	var i int
	for {
		select {
		case <-t5.C:
			fmt.Fprint(out, "Buzz\x1b[K")
		case <-t3.C:
			fmt.Fprint(out, "Fizz\x1b[K")
		case <-t1.C:
			i++
			fmt.Fprintf(out, "\n%d\r", i)
		}
	}
}

Discussion