Zen言語の標準ライブラリ紹介〜rand②〜

はじめに

けっこう標準ライブラリが充実しているわけですが、ドキュメントがないのがもったいないですね。 まとまった時間が取れないので、ちょこちょこ書いていくシリーズです。

リクエストあれば、優先する、かも?

乱数について続きを見ていきましょう。 前回は、Randomインタフェースと、それを実装する乱数生成器について、簡単な説明をしました。

今回は、Randomインタフェースの使い方です。

std.rand.bytes

bufで渡されたスライスをランダムなバイトで埋めます。 rRandomインタフェースを実装する乱数生成器を渡します。 これは、Randomインタフェースのfillを直接呼び出すのと同じです。

pub fn bytes(r: Random, buf: []u8) void
test "std.rand.bytes" {
    var x = rand.Xoroshiro128.init(0);
    var buf = [_]u8{ 0 } ** 10;

    rand.bytes(&x, &buf);
    debug.warn("random bytes = 0x{x}\n", .{buf});
}

std.rand.boolean

ランダムなブール値を取得します。

pub fn boolean(r: Random) bool

std.rand.int

ランダムなT型の整数値を取得します。

/// Returns a random int `i` such that `0 <= i <= maxInt(T)`.
/// `i` is evenly distributed.
pub fn int(r: Random, comptime T: type) T
test "std.rand.int" {
    var x = rand.Xoroshiro128.init(1);

    debug.warn("random i32 = {}\n", .{ rand.int(&x, i32) });
    debug.warn("random u1 = {}\n", .{ rand.int(&x, u1) });
}

その他

指定範囲の整数値を取得する関数など、色々あります。 一覧は公式ドキュメントをどうぞ。

zen-lang.org

Zen言語の標準ライブラリ紹介〜rand①〜

はじめに

けっこう標準ライブラリが充実しているわけですが、ドキュメントがないのがもったいないですね。 まとまった時間が取れないので、ちょこちょこ書いていくシリーズです。

リクエストあれば、優先する、かも?

乱数について見ていきましょう。

std.rand.Random

まずはRandomインタフェースです。 std.randの多くのAPIは、Randomインタフェースを実装する構造体インスタンスを必要とします。

Randomインタフェースは、ランダムな値でスライスを埋めるfill()関数を実装しなければなりません。

pub const Random = interface {
    fn fill(buf: []u8) void;
};

このRandomインタフェースを実装する構造体として、std.randには次の乱数生成器があります。

  • Xoroshiro128
  • Isaac64
  • Sfc64

例えば、Xoroshiro128を直接使うのであれば、次のようにinit()で初期化した後、fill()で乱数を取得します。

const std = @import("std");
const rand = std.rand;
const debug = std.debug;
const testing = std.testing;

test "std.rand.Xoroshiro128" {
    var x = rand.Xoroshiro128.init(0);
    var buf = [_]u8{ 0 } ** 10;

    x.fill(&buf);

    debug.warn("random value = 0x{x}\n", .{buf});
}

実行結果は次の通りです。

random value = 0xa333d71ca4469950fa4b

疑似乱数なので、この使い方だと毎回同じ値が出力されます。

Zen言語の標準ライブラリ紹介〜lazy〜

はじめに

けっこう標準ライブラリが充実しているわけですが、ドキュメントがないのがもったいないですね。 まとまった時間が取れないので、ちょこちょこ書いていくシリーズです。

リクエストあれば、優先する、かも?

グローバルなデータをスレッド安全に初期化するLazyInitです。

std.lazy.LazyInit

コンパイル時に初期値が決まらないグローバルデータを、安全に初期化するのは意外と面倒です。 LazyInitはそのような場合に、安全にグローバルデータを初期化することができます。

lazy.init() は任意の型Tに対して、LazyInit(T)の値を返します。 この時、T型の値は未定義です。

pub fn init(comptime T: type) LazyInit(T)

LazyInitget()resolve()の2つのメソッドを持ちます。

// LazyInit(T) {
    /// Returns a usable pointer to the initialized data,
    /// or returns null, indicating that the caller should
    /// perform the initialization and then call resolve().
    pub fn get(self: *Self) ?Ptr


    pub fn resolve(self: *Self) void

get()null が返ってきたら、値 (data フィールド) を初期化して、resolve() を呼びます。 内部は簡単なステートマシンを持っており、resolve()を呼ぶと、初期化済み状態になります。

const std = @import("std");
const lazy = std.lazy;
const testing = std.testing;

var global = lazy.init(u32);

test "std.lazy.LazyInit" {
    if (global.get()) |value| {
        // `resolve()`を呼ぶまでは `null` が返るので、ここには到達しない
        unreachable;
    } else {
        // `null` が取得できた場合、データを初期化して、`resolve`を呼ぶ
        global.data = 42;
        global.resolve();
    }

    if (global.get()) |value| {
        // get returns `*T`
        testing.equal(@to(u32, 42), value.*);
    } else {
        unreachable;
    }
}

注意点としては、get()nullが返ってきたら、必ず初期化しないといけない、ということです。 次のコードは無限ループに陥ります。

test "deadlock" {
    const S = struct {
        var g = lazy.init(u32);
    };
    if (S.g.get()) |_| {}
    if (S.g.get()) |_| {}
}

個人的には、lazy.init()が初期化関数を受け取れるようになっていて、最初に呼ばれたget()内で初期化関数呼び出してくれる方が良いなぁ、と思ったりします。

Zen言語の標準ライブラリ紹介〜sort②〜

はじめに

けっこう標準ライブラリが充実しているわけですが、ドキュメントがないのがもったいないですね。 まとまった時間が取れないので、ちょこちょこ書いていくシリーズです。

リクエストあれば、優先する、かも?

ソート関係ですが、ソート本体以外にstd.sortにあるものを紹介します。

std.sort.asc

<演算子が定義されている型Tに対して、fn (a: T, b: T) bool { return a < b } を計算する関数ポインタを返します。 コメントにあるように、sort()関数呼び出し時に引数渡しする比較関数を生成するために使います。

// Use these to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
pub fn asc(comptime T: type) fn (T, T) bool
const std = @import("std");
const sort = std.sort;
const testing = std.testing;

test "std.sort.asc" {
    const asc_u8 = sort.asc(u8);
    // true if `a` is less than `b`
    testing.ok(asc_u8(1, 2));
    testing.ok(!asc_u8(2, 2));
    testing.ok(!asc_u8(2, 1));
}

実装は次の通りなのですが、a < bを返さずに、generic.lessThanを使えば、構造体にも使えるようになる気が…?

pub fn asc(comptime T: type) fn (T, T) bool {
    const impl = struct {
        fn inner(a: T, b: T) bool {
            return a < b;
        }
    };

    return impl.inner;
}

現状では、構造体の型を渡すとコンパイルエラーになります。 次はコンパイルエラーです。

test "std.sort.asc for struct" {
    const S = struct {
        a: usize,
        b: f64,
    };
    const asc_s = sort.asc(S);
}
error[E09063]: operator not allowed for type 'S'
            return a < b;
                     ~

後で修正リクエスト投げてみましょう。

std.sort.desc

ascの逆版です。 fn (a: T, b: T) bool { return a > b } を計算する関数ポインタを返します。

pub fn desc(comptime T: type) fn (T, T) bool
test "std.sort.desc" {
    const asc_u8 = sort.desc(u8);
    // true if `a` is bigger than `b`
    testing.ok(asc_u8(2, 1));
    testing.ok(!asc_u8(2, 2));
    testing.ok(!asc_u8(1, 2));
}

std.sort.min / std.sort.argMin

任意型のスライスの中で最も小さい値を返します。 スライスの要素数0の場合のみ、nullを返します。

pub fn min(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?T

最も小さい値が格納されているインデックスを返します。

pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?usize

使い方は、次のような感じです。

test "std.sort.min" {
    const inputs = [_]i32{ 10, -5, 3, 20, -4 };
    testing.equal(@to(?i32, -5), sort.min(i32, &inputs, sort.asc(i32)));

    const position = sort.argMin(i32, &inputs, sort.asc(i32));
    testing.equal(@to(?usize, 1), position);
    testing.equal(@to(i32, -5), inputs[position.?]);
}

std.sort.max / std.sort.argMax

minのmax版です。

std.sort.isSorted

与えたスライスがソート済みかどうかをブール値で返します。

pub fn isSorted(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) bool
test "std.sort.isSorted" {
    const sorted = [_]i32{ -1, 0, 1, 2, 3, 4, 5 };
    const unsorted = [_]i32{ 2, 0, 1, -1, 3, 4, 5 };

    testing.ok(sort.isSorted(i32, &sorted, sort.asc(i32)));
    testing.ok(!sort.isSorted(i32, &unsorted, sort.asc(i32)));
}

Zen言語の標準ライブラリ紹介〜sort①〜

はじめに

けっこう標準ライブラリが充実しているわけですが、ドキュメントがないのがもったいないですね。 まとまった時間が取れないので、ちょこちょこ書いていくシリーズです。

リクエストあれば、優先する、かも?

ソート関係です。 次の3つの関数を紹介します。

  • binarySearch
  • insertionSort
  • sort

binarySearchstd.sort下で良いのでしょうかね? ソート済みであることが前提ではありますが。

std.sort.binarySearch

ソート済みのスライスitemから、keyの値を検索します。 値の比較はcompareFnで与える比較関数を使用します。 見つかればスライスのインデックスが、見つからない場合nullが返ります。

pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compareFn: fn (lhs: T, rhs: T) math.Order) ?usize

math.Orderは次のEnumです。

pub const Order = enum {
    /// Less than (`<`)
    lt,

    /// Equal (`==`)
    eq,

    /// Greater than (`>`)
    gt,
};

<, >, ==演算子をサポートする型に対しては、math.orderジェネリック関数になっており、共通して使用できます。

binarySearchの使用方法は次の通りです。

test "std.sort.binarySearch" {
    const input = [_]i32{ -10, -4, 0, 3, 5, 20 };
    // std.math.orderはジェネリック関数なので、型を確定させる
    const Cmp = struct {
        fn order_i32(lhs: i32, rhs: i32) std.math.Order {
            return std.math.order(lhs, rhs); 
        } 
    };

    testing.equal(
        @to(?usize, 1),
        sort.binarySearch(i32, -4, &input, Cmp.order_i32),
    );
}

std.sort.insertionSort

挿入ソートです。 アロケータなしで動くのが嬉しいです。 入力するitemsを直接入れ替えるので、itemsvarでないといけません (constじゃだめ) 。

/// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required).
pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void

sort.asc(i32)はアイテムを昇順に並び替えるための比較関数です。 次回やるので、そういうもんだと納得して下さい。

test "std.sort.insertionSort" {
    var items = [_]i32{ 20, -4, 0, 5, -10, 3 };
    const expect = [_]i32{ -10, -4, 0, 3, 5, 20 };

    sort.insertionSort(i32, items[0..], sort.asc(i32));

    testing.equalSlices(i32, &expect, &items);
}

std.sort.sort

ブロックソートです。 アロケータなしで動くのが嬉しいです。 しかも、O(n*log(n))です。 最高じゃないですか。

/// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required).
/// Currently implemented as block sort.
pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void

insertionSortと使い方は同じです。

test "std.sort.sort" {
    var items = [_]i32{ 5, 3, 1, 2, 4 };
    sort.sort(i32, items[0..], sort.asc(i32));

    testing.equalSlices(i32, &[_]i32{ 1, 2, 3, 4, 5 }, &items);
}

Zen言語の標準ライブラリ紹介〜time〜

はじめに

けっこう標準ライブラリが充実しているわけですが、ドキュメントがないのがもったいないですね。 まとまった時間が取れないので、ちょこちょこ書いていくシリーズです。

リクエストあれば、優先する、かも?

今日は時間関係です。

std.time.

std.time.sleep

普通のスリープ関数です。 引数は、スリープする秒数をナノ秒単位で与えます。

/// Spurious wakeups are possible and no precision of timing is guaranteed.
pub fn sleep(nanoseconds: u64) void
test "sleep" {
    // sleep 1 second
    time.sleep(1 * time.second);
}

ナノ秒単位で真面目に指定するのは大変ですが、定数が次のように定義されているので、必要に応じて使います。

/// Multiples of a base unit (nanoseconds)
pub const nanosecond = 1;
pub const microsecond = 1000 * nanosecond;
pub const millisecond = 1000 * microsecond;
pub const second = 1000 * millisecond;
pub const minute = 60 * second;
pub const hour = 60 * minute;

/// Divisions of a second
pub const ns_per_s = 1000000000;
pub const us_per_s = 1000000;
pub const ms_per_s = 1000;
pub const cs_per_s = 100;

/// Common time divisions
pub const s_per_min = 60;
pub const s_per_hour = s_per_min * 60;
pub const s_per_day = s_per_hour * 24;
pub const s_per_week = s_per_day * 7;

std.time.timestamp

秒単位のPOSIXタンムスタンプを取得します。

/// Get the posix timestamp, UTC, in seconds
pub fn timestamp() u64
test "timestamp" {
    debug.warn("timestamp = {}\n", .{ time.timestamp() });
    // sleep 1 second
    time.sleep(1 * time.second);

    debug.warn("timestamp = {}\n", .{ time.timestamp() });
}

出力は次のような感じです。タイムスタンプが1増えてますね。

timestamp = 1582431127
timestamp = 1582431128

std.time.milliTimestamp

ミリ秒版です。

pub fn milliTimestamp() u64

std.timer.Timer

簡易タイマーです。 イベントのハンドリングなどはできません。

Timer.start() もしくは Timer.reset() から経過した時間を計測できます。

pub const Timer = struct {
    /// Initialize the timer structure.
    pub fn start() Error!Timer

    /// Reads the timer value since start or the last reset in nanoseconds
    pub fn read(self: *Timer) u64

    /// Resets the timer value to 0/now.
    pub fn reset(self: *Timer) void

    /// Returns the current value of the timer in nanoseconds, then resets it
    pub fn lap(self: *Timer) u64
test "timer" {
    var timer = try time.Timer.start();

    const time_0 = timer.read();
    time.sleep(50 * time.millisecond);
    const time_1 = timer.read();
    testing.ok((time_1 - time_0) > 50 * time.millisecond);

    timer.reset();
    for ([_]u8 {0} ** 10) |_| {
        debug.warn("lap = {}\n", .{ timer.lap() });
    }
}

Zen言語の標準ライブラリ紹介〜generic〜

はじめに

けっこう標準ライブラリが充実しているわけですが、ドキュメントがないのがもったいないですね。 まとまった時間が取れないので、ちょこちょこ書いていくシリーズです。

リクエストあれば、優先する、かも?

どんな型でも比較できる便利な奴ら、genericを紹介します。

std.generic.equal

2つの引数、a, b が一致しているかどうかをブール値で返します。 この関数は任意の型に対して使用できますが、abとは同じ型でなければなりません。

型パラメータが不要なので、とっても便利です。

///Compares two of any type for equality. Containers are compared on a field-by-field basis,
/// where possible. Pointers are not followed.
pub fn equal(a: var, b: @TypeOf(a)) bool

利用例です。 整数型だろうが、配列だろうが、構造体だろうが、型だろうが、雑に突っ込めば善きに計らってくれます。

test "generic equal" {
    // integer
    testing.ok(generic.equal(10, 10));
    // floating point
    testing.ok(generic.equal(5.7, 5.7));
    // slice []const u8
    testing.ok(generic.equal("hello"[0..], "hello"[0..]));
    // type
    testing.ok(generic.equal(usize, usize));

    var a: usize = 10;
    // pointer
    testing.ok(generic.equal(&a, &a));

    const S = struct {
        a: u32 = 10,
        b: f64 = 5.7,
        c: []const u8 = "hello"[0..],
    };
    // struct
    testing.ok(generic.equal(S{}, S{}));

    const RefS = struct {
        ptr: *S,
    };
    // `s1` と `s2` は同じ値を持つ
    var s1 = S{};
    var s2 = S{};
    const ref_s1 = RefS{ .ptr = &s1 };
    const ref_s2 = RefS{ .ptr = &s2 };
    // Pointer is not followed.
    testing.ok(!generic.equal(ref_s1, ref_s2));
}

ただし、最後に示しているように、ポインタの指し先の値が一致しているかどうか、までは見てくれません。

std.generic.lessThan

a < bであるかどうかをブール値で返します。 equalと大体同じです。

///Compares two of any type for a lessThan order. Containers are compared on a field-by-field basis,
/// where possible. Pointers are not followed.
pub fn lessThan(a: var, b: @TypeOf(a)) bool