💬

マルチスレッドなふぃずばずお嬢様

2022/06/26に公開

スクラップで整理した内容を記事化。
FizzBuzzをマルチスレッドで実施して、かつふぃずばずお嬢様にしてみた。

マルチスレッドな処理について

Gleamの本家TOPページにある、「multi_threaded_hello_world」を参考にして実施する。

https://gleam.run/

まずは、プロジェクト作成。

$ gleam new multi_threaded_fizzbuzz
Your Gleam project multi_threaded_fizzbuzz has been successfully created.
The project can be compiled and tested by running these commands:

    cd multi_threaded_fizzbuzz
    gleam test

gleam/otpを利用できるようにする

マルチスレッドには、gleam/otp/processを利用する。

そして、gleam/otp/processを利用するには、モジュールgleam_otpを追加する。
これによって、erlangのotpが利用可能となる。

モジュールの追加には、gleam addコマンドを利用する。

$gleam add gleam_otp

  Resolving versions
Downloading packages
 Downloaded 2 packages in 0.05s
      Added gleam_otp v0.4.0

このコマンドを実行すると、gleam.tomlファイルのdependenciesの項目に、gleam_otpが追加されている。

gleam.toml
~(略)~

[dependencies]
gleam_stdlib = "~> 0.22"
gleam_otp = "~> 0.4"

~(以下略)~

FizzBuzzについて

FizzBuzz関数の実装。
ひとまず、mainとは別のファイルにして実装。

multi_threaded_fizzbuzz/src/fizzbuzz/fizzbuzz.gleam
import gleam/int

pub fn to_fizzbuzz(i) {
  case i % 3, i % 5 {
    0, 0 -> "FizzBuzz"
    0, _ -> "Fizz"
    _, 0 -> "Buzz"
    _, _ -> int.to_string(i)
  }
}

ついでにテストコードも書いとく。

テスト対象(今回の場合、fizzbuzz/fizzbuzz)を明示的にimportしないといけないのがちょっと気になるところ。

multi_threaded_fizzbuzz/test/fizzbuzz/fizzbuzz_test.gleam
import fizzbuzz/fizzbuzz
import gleeunit/should

pub fn to_fizzbuzz_test() {
  fizzbuzz.to_fizzbuzz(1)
  |> should.equal("1")

  fizzbuzz.to_fizzbuzz(3)
  |> should.equal("Fizz")

  fizzbuzz.to_fizzbuzz(5)
  |> should.equal("Buzz")

  fizzbuzz.to_fizzbuzz(15)
  |> should.equal("FizzBuzz")

  fizzbuzz.to_fizzbuzz(40)
  |> should.equal("Buzz")

  fizzbuzz.to_fizzbuzz(42)
  |> should.equal("Fizz")

  fizzbuzz.to_fizzbuzz(43)
  |> should.equal("43")

  fizzbuzz.to_fizzbuzz(45)
  |> should.equal("FizzBuzz")
}

gleam testコマンドで、テストを実行してみる。

$gleam test
  Compiling multi_threaded_fizzbuzz
   Compiled in 1.33s
    Running multi_threaded_fizzbuzz_test.main
..
Finished in 0.016 seconds
2 tests, 0 failures

お嬢様の語尾について

何をすれば「ふぃずばずお嬢様」になるのかよくわかっていないので、とりあえず語尾をお嬢様っぽくしてみることにした。

FizzBuzzに利用する数値が、奇数なら「ですわ〜」偶数なら「ですのよー」 を出力することにした。

ひとまずは、fizzbuzz/fizzbuzzへ関数を用意することにした。

multi_threaded_fizzbuzz/src/fizzbuzz/fizzbuzz.gleam
〜(上記略)〜

pub fn to_gobi(i) {
  case int.is_odd(i) {
    True -> "ですわ〜"
    False -> "ですのよー"
  }
}

テストコードも追記。

multi_threaded_fizzbuzz/test/fizzbuzz/fizzbuzz_test.gleam
〜(上記略)〜

pub fn to_gobi_test() {
  fizzbuzz.to_gobi(1)
  |> should.equal("ですわ〜")

  fizzbuzz.to_gobi(2)
  |> should.equal("ですのよー")

  fizzbuzz.to_gobi(8)
  |> should.equal("ですのよー")

  fizzbuzz.to_gobi(9)
  |> should.equal("ですわ〜")
}

gleam testを実行。こちらも、とくに問題なし。

$gleam test
  Compiling multi_threaded_fizzbuzz
   Compiled in 1.23s
    Running multi_threaded_fizzbuzz_test.main
...
Finished in 0.018 seconds
3 tests, 0 failures

メイン処理

最初にふれたgleam/otp/processを利用してマルチスレッドを実施する。

メイン処理中で、今回作成した「FizzBuzz」と「語尾」を組み込む。

multi_threaded_fizzbuzz/src/multi_threaded_fizzbuzz.gleam
import gleam/io
import gleam/int
import gleam/list
import gleam/string
import gleam/otp/process
import fizzbuzz/fizzbuzz

pub fn main() {
  list.range(1, 100)
  |> list.map(start_process)
  |> list.map(process.monitor_process)
  |> list.each(process.receive(_, 3))
  // Wait for them to finish
}

fn start_process(i) {
  process.start(fn() {
    let fizzbuzz_msg = fizzbuzz.to_fizzbuzz(i)
    let gobi_msg = fizzbuzz.to_gobi(i)
    let message =
      string.concat([int.to_string(i), " は ", fizzbuzz_msg, gobi_msg])
    io.println(message)
  })
}

gleam runで実行。以下のような結果を得た。

$gleam run
  Compiling multi_threaded_fizzbuzz
   Compiled in 1.47s
    Running multi_threaded_fizzbuzz.main
11ですわ〜
22ですのよー
99 は Fizzですわ〜
44ですのよー
20 は Buzzですのよー
40 は Buzzですのよー
4141ですわ〜
42 は Fizzですのよー
4343ですわ〜
4444ですのよー
45 は FizzBuzzですわ〜
〜(略)〜

コード一式

ひとまず、ソースコード一式を記載する。

multi_threaded_fizzbuzz/src/multi_threaded_fizzbuzz.gleam
import gleam/io
import gleam/int
import gleam/list
import gleam/string
import gleam/otp/process
import fizzbuzz/fizzbuzz

pub fn main() {
  list.range(1, 100)
  |> list.map(start_process)
  |> list.map(process.monitor_process)
  |> list.each(process.receive(_, 3))
  // Wait for them to finish
}

fn start_process(i) {
  process.start(fn() {
    let fizzbuzz_msg = fizzbuzz.to_fizzbuzz(i)
    let gobi_msg = fizzbuzz.to_gobi(i)
    let message =
      string.concat([int.to_string(i), " は ", fizzbuzz_msg, gobi_msg])
    io.println(message)
  })
}
multi_threaded_fizzbuzz/src/fizzbuzz/fizzbuzz.gleam
import gleam/int

pub fn to_fizzbuzz(i) {
  case i % 3, i % 5 {
    0, 0 -> "FizzBuzz"
    0, _ -> "Fizz"
    _, 0 -> "Buzz"
    _, _ -> int.to_string(i)
  }
}

pub fn to_gobi(i) {
  case int.is_odd(i) {
    True -> "ですわ〜"
    False -> "ですのよー"
  }
}
multi_threaded_fizzbuzz/test/fizzbuzz/fizzbuzz_test.gleam
import fizzbuzz/fizzbuzz
import gleeunit/should

pub fn to_fizzbuzz_test() {
  fizzbuzz.to_fizzbuzz(1)
  |> should.equal("1")

  fizzbuzz.to_fizzbuzz(3)
  |> should.equal("Fizz")

  fizzbuzz.to_fizzbuzz(5)
  |> should.equal("Buzz")

  fizzbuzz.to_fizzbuzz(15)
  |> should.equal("FizzBuzz")

  fizzbuzz.to_fizzbuzz(40)
  |> should.equal("Buzz")

  fizzbuzz.to_fizzbuzz(42)
  |> should.equal("Fizz")

  fizzbuzz.to_fizzbuzz(43)
  |> should.equal("43")

  fizzbuzz.to_fizzbuzz(45)
  |> should.equal("FizzBuzz")
}

pub fn to_gobi_test() {
  fizzbuzz.to_gobi(1)
  |> should.equal("ですわ〜")

  fizzbuzz.to_gobi(2)
  |> should.equal("ですのよー")

  fizzbuzz.to_gobi(8)
  |> should.equal("ですのよー")

  fizzbuzz.to_gobi(9)
  |> should.equal("ですわ〜")
}

まとめ

ただFizzBuzzを書くのも面白みがなかったので、お嬢様口調にしてみましたわ。

余談ですが、rangeの範囲を「range(1,10000)」で試したみたら、3000あたりまでしか動作しなかった。
どこかでストッパーになっている関数があるのかな。別途調べてみよう。

。。。ではなくて、調べてみますわ。

Discussion