エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

aws-sdkで取得できるタグを扱いやすくするための覚書

動機

AWS のリソースの多くは key-value の組みをタグとして設定できるようになっているのですが。

例えば EC2 インスタンスを取得する aws-sdk のメソッド Aws::EC2::Client#describe_instances のレスポンスは次のようになっています。

resp.reservations[0].instances[0].tags #=> Array
resp.reservations[0].instances[0].tags[0].key #=> String
resp.reservations[0].instances[0].tags[0].value #=> String

docs.aws.amazon.com

つまり辞書形式になっているわけではなく、単に key と value のペアを格納するクラスの配列にすぎません。

実装を紐解いてみると、次のような構造になっていました。

class Tag < Struct.new(:key, :value)
end

key から value を引きたい場合、たとえば次のようなコードを書くことになります。

tags = [
  Tag.new("foo", 123),
  Tag.new("bar", 456),
  Tag.new("baz", 789)
]

pp tags.find { |tag| tag.key == "foo" }&.value  # => 123
pp tags.find { |tag| tag.key == "bar" }&.value  # => 456
pp tags.find { |tag| tag.key == "baz" }&.value  # => 789
pp tags.find { |tag| tag.key == "hoge" }&.value # => nil

これがもう少しどうにかならないか、というのが今回のお題です。

クラスで包む

Tag の配列を内部に持ち、key-value アクセスを容易にするメソッドを用意するパタンです。

class Tags
  def self.[](tags)
    new(tags)
  end

  def initialize(tags)
    @tags = tags
  end

  def [](key)
    @tags.find { |tag| tag.key == key }&.value
  end
end

pp Tags[tags]["foo"]  # => 123
pp Tags[tags]["bar"]  # => 456
pp Tags[tags]["baz"]  # => 789
pp Tags[tags]["hoge"] # => nil

これは C++ のときに割と好んで利用していた方法です。

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

struct Tag {
    std::string key;
    int value;
};

class Tags {
public:
    Tags(const std::vector<Tag>& tags) : tags_(tags) {}

    int operator [] (const std::string& key) {
        auto tag = std::find_if(tags_.begin(), tags_.end(), [&](const Tag& tag) { return tag.key == key; });
        return tag->value;
    }

private:
    const std::vector<Tag>& tags_;
};

int main(int, char*[]) {
    auto tags = {
        Tag { "foo", 123 },
        Tag { "bar", 456 },
        Tag { "baz", 789 }
    };

    std::cout << Tags(tags)["foo"] << std::endl;
    std::cout << Tags(tags)["bar"] << std::endl;
    std::cout << Tags(tags)["baz"] << std::endl;
}

これは、動的にクラスのメソッドを変更できない環境ではよくある方法と思いますが、Ruby では個々のオブジェクトにも固有のメソッドを追加できることを利用して、tags オブジェクトにメソッドを追加してみようと思います。

オブジェクトを拡張する (1)

今回はメソッドを定義する Tags をモジュールとして定義し、Object#extend を利用して tags オブジェクトにメソッドを組み込んでいます。

module Tags
  def [](key)
    find { |tag| tag.key == key }&.value
  end
end

tags.extend(Tags)

pp tags["foo"]  # => 123
pp tags["bar"]  # => 456
pp tags["baz"]  # => 789
pp tags["hoge"] # => nil

ただし #[] を上書きしてしまうため Array として機能しなくなるというかなり重度な欠点を抱えます。 Array と被らない名前を選べばよい話ではあるのですが。

pp tags[0] # => nil
pp tags[1] # => nil
pp tags[2] # => nil

オブジェクトを拡張する (2)

より Ruby ならではの方法といえば、やはり BasicObject#method_missing のオーバーライド。 アクセスできるキーがメソッドとして記述できる識別子に限られるという制約がありますが、オブジェクトの属性のように扱うことができるのでとても強力です。

module Tags
  def method_missing(name)
    key = name.to_s
    find { |tag| tag.key == key }&.value
  end
end

tags.extend(Tags)

pp tags.foo  # => 123
pp tags.bar  # => 456
pp tags.baz  # => 789
pp tags.hoge # => nil

Phoenix LiveView 0.18 の新しい構文の覚書とQRコード

Phoenix LiveView 0.18 の構文をいじっています。

具体的にはこれ。

hexdocs.pm

:if and :for

It is a syntax sugar for <%= if .. do %> and <%= for .. do %> that can be used in regular HTML, function components, and slots.

For example in an HTML tag:

<table id="admin-table" :if={@admin?}>
  <tr :for={user <- @users}>
    <td><%= user.name %>
  </tr>
<table>

この形の構文は、従来の Phoenix でも、ふだん仕事で使っている Ruby on Rails でも出てこない形なので、今でもこれを見るとむずむずする感じがあるのですが、入れ子が浅くなることで、反復する要素を実際の深さのレベルで書けるという点はわかりやすくてよいですね。

と、いうわけで。 LiveView 0.18 でQRコードを表示させてみました。

defmodule MyAppWeb.QrLive do
  use MyAppWeb, :live_view

  def mount(_, _, socket) do
    {:ok, assign(socket, view_box: "", size: 0, cells: [])}
  end

  def render(assigns) do
    ~H"""
    <form phx-change="change"><textarea name="qr[text]" /></form>

    <svg viewBox={@view_box} xmlns="http://www.w3.org/2000/svg" fill="black">
      <rect x="0" y="0" width={@size} height={@size} fill="white" />
      <rect :for={{x, y} <- @cells} x={x} y={y} width="1" height="1" />
    </svg>
    """
  end

  def handle_event("change", %{"qr" => %{"text" => text}}, socket) do
    socket =
      case QRCode.create(text) do
        {:ok, %QRCode.QR{matrix: matrix}} ->
          size = length(matrix)

          cells =
            for {row, y} <- Enum.with_index(matrix),
                {cell, x} <- Enum.with_index(row),
                cell == 1,
                do: {x, y}

          assign(socket, view_box: "0 0 #{size} #{size}", size: size, cells: cells)

        _ ->
          socket
      end

    {:noreply, socket}
  end
end

実行例。

コンソールにQRコードを表示したい

Webアプリケーションを開発しているとき、携帯端末での表示を確認したくなるときがあります。 ブラウザのレスポンシブ・デザイン・モードを利用すれば、デスクトップでも見た目の確認はできますが、やはり手のひらの中でどのように表示されるかを知るには、端末そのものに表示させるのが一番です。

そのようなときにURLを携帯端末に送るため、コンソールにQRコードを表示する簡単なスクリプトを書いてみました。

自分で書かなくても、完成度の高いツールは巷に溢れていると思いますが、今後QRコードをブラウザに表示したり、メールで送信したりする必要に迫られることもないとは言えないので、そのトレーニングの意味合いも込めて。

とは言え、QRコードエンコーディングをすべて書くのは大変なので、そこはパッケージを利用し、表現のところだけ自分で実装しています。

エンコーディングのパッケージは Hex に公開されている qr_code を利用しました。

hex.pm

Elixir 1.12 からは Mix.install/2 が実装されて、パッケージを簡単に利用できるようになりました。 これも、スクリプトを書くハードルを下げてくれた気がします。

# qr.exs

Mix.install([:qr_code])

defmodule QR do
  @white IO.ANSI.light_white_background()
  @black IO.ANSI.black_background()

  def show_as_qr_code(str) do
    IO.puts(str)

    {:ok, %{matrix: matrix}} =
      str
      |> QRCode.create()

    len = length(hd(matrix))

    edge = [@white, String.duplicate("  ", len + 2), @black]

    IO.puts(edge)

    matrix
    |> Enum.each(fn row ->
      IO.write([@white, "  ", @black])

      row
      |> Enum.map(fn
        1 -> [@black, "  "]
        0 -> [@white, "  "]
      end)
      |> IO.write()

      IO.puts([@white, "  ", @black])
    end)

    IO.puts(edge)
  end
end

System.argv()
|> Enum.each(&QR.show_as_qr_code/1)

スクリプトを書いたら elixir コマンドで実行です。

$ elixir qr.exs https://elixir-lang.org

初回だけ、パッケージのインストールが実行された後にQRコードが表示されます。

2回目以降は、すぐに結果を表示してくれるはずです。

Elixirのドキュメントでガードをグルーピングするときの覚書

ドキュメントを生成した時に、defguard で定義するガードをグルーピングするときの設定について、いつも忘れてしまい自分の以前のリポジトリを見返すことがたびたびなので、こちらの覚書として記録しておきます。

ガードと関数をモジュールに記述した場合、

defmodule FizzBuzz do
  defguard is_pos_integer(n) when is_integer(n) and n > 0
  defguard is_fizz(n) when is_pos_integer(n) and rem(n, 3) == 0
  defguard is_buzz(n) when is_pos_integer(n) and rem(n, 5) == 0

  def fizz_buzz(n) when is_fizz(n) and is_buzz(n), do: "Fizz Buzz"
  def fizz_buzz(n) when is_fizz(n), do: "Fizz"
  def fizz_buzz(n) when is_buzz(n), do: "Buzz"
  def fizz_buzz(n) when is_pos_integer(n), do: to_string(n)
end

何も指定せず ExDoc でドキュメントを生成すると、ガードも Functions にまとめられます。

ドキュメントを紐解くと、@doc 属性と :groups_for_functions を指定することで関数を任意のグループにグルーピングできると書かれています。

hexdocs.pm

ドキュメントにはガードへの言及はないのですが、defguard の実装を確認してみると、@doc guard: true のの設定が確認できます。

結論として、mix.exs で次のように :groups_for_functions を指定すると、ガードを独立したグループにグルーピングすることができるようになります。

defmodule FizzBuzz.MixProject do
  use Mix.Project

  def project do
    [
      app: :fizz_buzz,
      version: "0.1.0",
      elixir: "~> 1.13",
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      docs: docs() # 追加
    ]
  end

  # ...中略...

  # 追加
  defp docs do
    [
      groups_for_functions: [
        Guards: & &1[:guard]
      ]
    ]
  end
end

Elixirでmutableなバイト列を扱う(Rustの力を借りて)

先日のブログに書いた通り、いつ以来かのセルラオートマトンに手を出しています。 そのときの記事の実装では、更新処理に Rust を利用してはいるものの、世代ごとにimmutableなバイト列を生成していました。

Erlang のアロケータを利用するので、確保したメモリの解放忘れのような心配はありませんが、効率を考えるとあまりうれしくない。

というわけで。 今回は、バイト列は Rust で確保し、関数の呼び出しを介して Elixir からそのバイト列を操作してみよう、という回です。

ハンドルあるいはリファレンス

まず一般的な話として。 一方で確保したリソースを、もう一方で利用するばあい、ハンドルを渡すことが常套手段として考えられます。

今回のケースであれば、Rust でメモリ領域を確保し、その情報をハンドルとして Elixir に渡し、Elixir は受け取ったハンドルとその他のパラメータを引数にして Rust の関数を呼び出す、という手順です。

Rustler にはちょうど、Rust でファイルのオープンと読み込みを実装し、それを Elixir から利用するというサンプルが公開されています。 ファイルハンドルを Rust と Elixir の間でやり取りすることで、Elixir から Rust のファイル操作を利用することができるようになっています。

github.com

ちなみに。 ここまでハンドルという言葉を使いましたが、このような用途の値はElixirではリファンレンス型の値として扱われますので、以下リファレンスという言葉で話を進めることにします。

MutableBinary.NIF モジュール

今回も Rust との接続には Rustler を使います。

hex.pm

コード量はそれほど多いわけではありませんが、Elixir と Rust の両方にまたがって実装するので、設定などのためにファイルの数が増えてしまいます。 コード全体は GitHub に上げましたのでそちらを参照ください。

github.com

GitHubリポジトリは、依存パッケージに直接指定することができます。 もし興味がありましたら、新しくプロジェクトを作成して試してみてください。

# mix.exs

  defp deps do
    [
      {:mutable_binary, github: "mattsan/mutable_binary"}
    ]
  end

以降では、注目するコードを抜粋して見ていきたいと思います。

Rust の実装

Rust の実装から見てゆきましょう。

ファイルは、リポジトリnative/mutable_binary_nif/src/lib.rs です。

構造体定義

まず可変なバイト列を格納する構造体を定義します。

後述する初期化のコードで、マクロを利用して Elixir との間で値をやり取りするために必要な種々のコードを生成する…ようです。 ドキュメントの記述が少ないのと、わたしが Rust を学んでいる途中ということで、深いツッコミは今回のところはご容赦を。

struct MutableBinaryResource {
    pub stream: Mutex<Vec<u8>>,
}

バイト列を生成する

先に定義した構造体を利用してバイト列を生成します。

このとき、構造体の値を rustler::resource::ResourceArc という構造体で包みます。 これは、ドキュメントに「std::sync::Arc のようなもの」とあるように、リファレンスカウンタでリソースを管理する構造体のようです。 Drop トレイトが実装されていてガベージコレクションによって解放されるとのこと。

Elixir で扱える形式に変換できる値には rustler::types::Encoder トレイトが実装されていて、encode で変換できるようになっています。

ここでは Rust のタプルを Elixir のタプルに変換するために利用しています。

#[rustler::nif]
fn new(env: Env, size: usize) -> Term {
    if size > 0 {
        let resource = ResourceArc::new(MutableBinaryResource {
            stream: Mutex::new(vec![0; size]),
        });

        (atoms::ok(), resource).encode(env)
    } else {
        (atoms::error(), atoms::out_of_range()).encode(env)
    }
}

リソースを確保できたばあい、アトム :ok とリソースのタプルを返しますが、これを Elixir では アトム :ok とリファレンスの値のタプルとして受け取ります。 確保したバイト列の操作をするときに、このリファレンスを関数の引数として渡します。

指定した位置のバイト値を読み出す

リファレンスとインデクスを引数に受け取り、インデクスの位置のバイトの値を返します。

Elixir が渡したリファレンスは、Rust では元のリソースの形式である ResourceArc<MutableBinaryResource> として受け取ることになります。

この値をロックして元々のバイト列の構造体を取り出します。

インデクスがバイト列の範囲内のばあいは、アトム :ok とバイト列のインデクスで指定されたバイトの値のタプルを encode して返します。

#[rustler::nif]
fn get(env: Env, resource: ResourceArc<MutableBinaryResource>, index: usize) -> Term {
    let resource_struct = resource.stream.try_lock().unwrap();

    if index < resource_struct.len() {
        (atoms::ok(), resource_struct[index]).encode(env)
    } else {
        (atoms::error(), atoms::out_of_range()).encode(env)
    }
}

書き込む、長さを取得する、バイナリとして取得する

これ以外の操作も、基本的に手順になります。

Elixir 側でリファレンスとして渡される ResourceArc<MutableBinaryResource> を受け取り、ロックして Rust の値として取り出し、操作する。

リポジトリに置いたコードには、get の他に set, length, to_string という関数を定義しています。 これらの詳細はリポジトリを参照してみてください。

初期化する

構造体の定義のところで書いたように、マクロを使って必要なコードを生成します。 そしてそのコードを実装に持つ関数を定義します。

fn load(env: Env, _: Term) -> bool {
    rustler::resource!(MutableBinaryResource, env);
    true
}

この関数は、公開する関数と一緒に NIF を初期化する関数に渡されます。

rustler::init!(
    "Elixir.MutableBinary.NIF",
    [new, length, set, get, to_string],
    load = load
);

これで Rust 側の実装は終わりました。

Elixir の実装

次に Elixir の実装です。

ファイルはリポジトリlib/mutable_binary/nif.ex です。

Rust の実装との接続は Rustler が面倒を見てくれるので、Elixir の実装は多くはありません。 Rustleruse しエントリとなる関数を定義するだけで終わってしまいます。 エントリの関数の定義も、単に NIF をロードできなかったばあいのためにエラーを返すだけのシンプルなものです。

defmodule MutableBinary.NIF do
  use Rustler, otp_app: :mutable_binary, crate: :mutable_binary_nif

  def new(_), do: err()
  def length(_), do: err()
  def get(_, _), do: err()
  def set(_, _, _), do: err()
  def to_string(_), do: err()

  defp err, do: :erlang.nif_error(:nif_not_loaded)
end

実演

簡単なスクリプトを書いて実行してみます。

# sample.exs

{:ok, ref} = MutableBinary.NIF.new(16)

MutableBinary.NIF.length(ref) |> IO.inspect(label: "length")
MutableBinary.NIF.to_string(ref) |> IO.inspect(label: "to_string")

MutableBinary.NIF.get(ref, 0) |> IO.inspect(label: "get [0]")
MutableBinary.NIF.set(ref, 0, 123) |> IO.inspect(label: "set [0] <- 123")
MutableBinary.NIF.get(ref, 0) |> IO.inspect(label: "get [0]")
MutableBinary.NIF.get(ref, 16) |> IO.inspect(label: "get [16]")

MutableBinary.NIF.set(ref, 0, 69)
MutableBinary.NIF.set(ref, 1, 108)
MutableBinary.NIF.set(ref, 2, 105)
MutableBinary.NIF.set(ref, 3, 120)
MutableBinary.NIF.set(ref, 4, 105)
MutableBinary.NIF.set(ref, 5, 114)
MutableBinary.NIF.set(ref, 6, 33)
MutableBinary.NIF.set(ref, 7, 32)
MutableBinary.NIF.set(ref, 8, 38)
MutableBinary.NIF.set(ref, 9, 32)
MutableBinary.NIF.set(ref, 10, 82)
MutableBinary.NIF.set(ref, 11, 117)
MutableBinary.NIF.set(ref, 12, 115)
MutableBinary.NIF.set(ref, 13, 116)
MutableBinary.NIF.set(ref, 14, 33)
MutableBinary.NIF.set(ref, 15, 33)

MutableBinary.NIF.to_string(ref) |> IO.inspect(label: "to_string")

実行に先立って、パッケージの取得とコンパイル

$ mix do deps.get, compile

mix runスクリプトを実行します。

$ mix run sample.exs
length: 16
to_string: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>
get [0]: {:ok, 0}
set [0] <- 123: :ok
get [0]: {:ok, 123}
get [16]: {:error, :out_of_range}
to_string: "Elixir! & Rust!!"

何も不思議なところはないはずなのですが、Elixir で mutable な値を利用できるというだけで、ちょっと新鮮な感じがしてくるので不思議です。

ちなみに。 GitHub に上げたコードには、MutableBinary.NIF モジュールを背後に隠して、外部とインタフェースする MutableBinary モジュールを用意しています。

いつか読むはずっと読まない:素数が香り、形がきこえる

ライフゲイム Conway's Game of Life で知られる J.H.コンウェイの著作。

新装改題され昨年末に出版されたもの。 書店で偶然見つけ、何かの縁を感じて購入。

Rustlerでコンウェイの Game of Life を書く

かつてセルラオートマトンに魅了されていた時期がありまして。

熱を上げていた時期はそれほど長くはなかったのですが、その後も伴奏のように背景でずっと鳴り続けていました。

最近、セルラオートマトンの新しい本を手に入れて、再び熱が上がってきています。

まずは手始めに。 このところ気に入って使っているElixirと、高速化のためのRustを使って、コンウェイGame of Lifeを再実装してみることにしました。

心臓部をRustで実装する

「Elixirと、高速化のためのRust」、と言っているそばから心臓部はRustでの実装です。

ElixirからRustの実装を利用するにならrustler。 というわけで、rustlerで生成した雛形にGame of Lifeの心臓部、現在の状態から次の状態を生成する部分をRustで実装します。

hex.pm

// native/game_of_life_nif/src/lib.rs

use rustler::Binary;
use rustler::OwnedBinary;

#[rustler::nif]
fn next(field: Binary, width: usize, height: usize) -> OwnedBinary {
    let mut next_field = OwnedBinary::new(width * height).unwrap();

    next_field.fill(0);
    for y in 0..height {
        for x in 0..width {
            let mut count = 0u8;
            let left = (x + width - 1) % width;
            let right = (x + 1) % width;
            let top = (y + height - 1) % height;
            let bottom = (y + 1) % height;

            count += field[top * width + left];
            count += field[top * width + x];
            count += field[top * width + right];

            count += field[y * width + left];
            count += field[y * width + right];

            count += field[bottom * width + left];
            count += field[bottom * width + x];
            count += field[bottom * width + right];

            if field[y * width + x] == 0 {
                next_field[y * width + x] = if count == 3 { 1 } else { 0 };
            } else {
                next_field[y * width + x] = if count == 2 || count == 3 { 1 } else { 0 };
            }
        }
    }

    next_field
}

rustler::init!("Elixir.GameOfLife.Nif", [next]);

現在の状態をあらわすバイナリと幅と高さを受け取り、次の状態をあらわすバイナリを返します。

右と左、上と下が接続している、いわゆるトーラスとしてあつかっています。 Rustも学習途上で、naiveな実装になっていますが、そこはご容赦を。

次にRustの実装を読み込み、関数を呼び出せるようにするためのElixirのモジュールを実装します。

# lib/game_of_life/nif.ex

defmodule GameOfLife.Nif do
  @moduledoc false
  use Rustler, otp_app: :game_of_life, crate: :game_of_life_nif

  def next(_field, _width, _height), do: :erlang.nif_error(:nif_not_loaded)
end

きちんと機能するか試してみます。

次のようなスクリプトを用意します。 見ての通りblinkerの実装です。

# blinker.exs

~w(
  0 0 0 0 0
  0 0 1 0 0
  0 0 1 0 0
  0 0 1 0 0
  0 0 0 0 0
)
|> Enum.into(<<>>, &<<String.to_integer(&1)>>)
|> GameOfLife.Nif.next(5, 5)
|> String.to_charlist()
|> Enum.chunk_every(5)
|> IO.inspect()

これを実行すると、次の状態をえられることが確認できました。

$ mix run blinker.exs
[
  [0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0],
  [0, 1, 1, 1, 0],
  [0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0]
]

入力しやすくする、出力を見やすくする

肝となる部分は GameOfLife.Nif.next/3 ですべてなので、結果のバイナリを次の段の入力にすれば、繰り返した分だけの世代の状態をえることができます。

とはいえ入力や出力を、もっと人が扱いやす形にしたい。

と、いうわけで。 文字の並びから入力となるバイナリを生成するシジルと、結果を文字の並びとして出力する関数を追加してみました。

ついでに GameOfLife.Nif.next/3 へ移譲して GameOfLife.next/3 という自然な記述で呼び出せるようにしました。

defmodule GameOfLife do
  defdelegate next(field, width, height), to: GameOfLife.Nif

  defmacro sigil_F({:<<>>, _meta, [string]}, _) do
    quote bind_quoted: [string: string] do
      for <<c <- string>>, c not in ' \r\n\t', into: <<>> do
        case c do
          ?@ -> <<1>>
          _ -> <<0>>
        end
      end
    end
  end

  def show(field, width, height) do
    for <<c <- field>> do
      case c do
        1 -> "@ "
        _ -> '. '
      end
    end
    |> Stream.chunk_every(width)
    |> Enum.each(&IO.puts/1)

    field
  end
end

先ほどの blinker を次のように書き換えます。

~F は、@1 に、それ以外の文字を 0 としてバイナリを生成します。 空白文字(空白、改行、タブ)は無視するので、自由にスペーシングや改行を含めることができます。

show/3 は、バイナリの内容の 0.1@ で、指定された幅と高さで出力します。 また入力のバイナリをそのまま戻り値とするので、パイプで繋げて次の段の入力にすることができます。

# blinker.exs

import GameOfLife

~F(
  . . . . .
  . . @ . .
  . . @ . .
  . . @ . .
  . . . . .
)
|> show(5, 5)
|> GameOfLife.next(5, 5)
|> show(5, 5)
|> GameOfLife.next(5, 5)
|> show(5, 5)

実行。

$ mix run blinker.exs
. . . . . 
. . @ . . 
. . @ . . 
. . @ . . 
. . . . . 
. . . . . 
. . . . . 
. @ @ @ . 
. . . . . 
. . . . . 
. . . . . 
. . @ . . 
. . @ . . 
. . @ . . 
. . . . . 

定番のgliderも。

# glider.exs

import GameOfLife

~F(
  . @ . . .
  . . @ . .
  @ @ @ . .
  . . . . .
  . . . . .
)
|> show(5, 5)
|> GameOfLife.next(5, 5)
|> show(5, 5)
|> GameOfLife.next(5, 5)
|> show(5, 5)
|> GameOfLife.next(5, 5)
|> show(5, 5)
|> GameOfLife.next(5, 5)
|> show(5, 5)
|> GameOfLife.next(5, 5)
|> show(5, 5)
|> GameOfLife.next(5, 5)
|> show(5, 5)

実行。

$ mix run glider.exs
. @ . . . 
. . @ . . 
@ @ @ . . 
. . . . . 
. . . . . 
. . . . . 
@ . @ . . 
. @ @ . . 
. @ . . . 
. . . . . 
. . . . . 
. . @ . . 
@ . @ . . 
. @ @ . . 
. . . . . 
. . . . . 
. @ . . . 
. . @ @ . 
. @ @ . . 
. . . . . 
. . . . . 
. . @ . . 
. . . @ . 
. @ @ @ . 
. . . . . 
. . . . . 
. . . . . 
. @ . @ . 
. . @ @ . 
. . @ . . 
. . . . . 
. . . . . 
. . . @ . 
. @ . @ . 
. . @ @ . 

悪くない感じ。

最初に書いたようにnaiveな実装なので、ElixirやRustの利点を活かした実装 − 複数のプロセスで処理するとか − をしてみたいと考えています。

いつか読むはずっと読まない:ライフゲイム

en.wikipedia.org

John Horton Conway FRS (26 December 1937 – 11 April 2020) was an English mathematician active in the theory of finite groups, knot theory, number theory, combinatorial game theory and coding theory.

...

On 11 April 2020, at age 82, he died of complications from COVID-19.

熱を上げるきっかけの一つになった本(の新装版)。

自己の責任と能力を的確に把握し…

先日、仕事中で。よいプログラマであるために心に留めていることをプロジェクトのメンバに話す機会がありました。

よくあるためにと意識するものは多々ありますが、かつて伺ったこの話がいつもついて回っています。

  • 自己の責任と能力を的確に認識し…個人として責任を持つ
  • 継続的な学習、能力開発

blog.emattsan.org

当時のブログにも書いたように、これはプログラマのことではなく、看護師の倫理綱領です。

話をした機会に元の綱領を読み返してみました。

www.nurse.or.jp

人々の権利を尊重し、人々が自らの意向や価値観にそった選択ができるよう支援する。

自己の責任と能力を的確に把握し、実施した看護について個人としての責任をもつ。

常に、個人の責任として継続学習による能力の開発・維持・向上に努める。

より質の高い看護を行うため、看護職自身のウェルビーイングの向上に努める。

分野は違えど、規範として心に留めておきたいと、改めて感じます。