Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 16850] ファイルの概要

このコミットは、Go言語の math/rand パッケージにおけるドキュメンテーションと例の改善を目的としています。具体的には、パッケージの概要に Seed 関数の重要性を明記し、トップレベルの乱数生成関数(rand.Intn など)がデフォルトの共有 Source を使用していることを明確にするためのコメントが追加されました。また、rand.Seed の使用例を示す新しいトップレベルの Example 関数が example_test.go に追加されています。これにより、ユーザーが math/rand パッケージをより適切に理解し、決定論的な乱数シーケンスを避けるために Seed を適切に利用できるよう促しています。

コミット

commit 6efb6b9e381405d0fcae6cd098ab59dfc98dc8c3
Author: Andrew Gerrand <adg@golang.org>
Date:   Tue Jul 23 16:07:28 2013 +1000

    math/rand: mention Seed in overview, add another top-level example
    
    Fixes #5937.
    
    R=golang-dev, mirtchovski, r
    CC=golang-dev
    https://golang.org/cl/11705043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/6efb6b9e381405d0fcae6cd098ab59dfc98dc8c3

元コミット内容

math/rand: mention Seed in overview, add another top-level example
    
Fixes #5937.
    
R=golang-dev, mirtchovski, r
CC=golang-dev
https://golang.org/cl/11705043

変更の背景

この変更は、GoのIssue #5937「math/rand のドキュメントは、Seed を呼び出さないと決定論的になることを明確にする必要がある」に対応するものです。

math/rand パッケージは、デフォルトで固定されたシード値(通常は 1)を使用して初期化されるグローバルな乱数ジェネレータを提供します。これは、プログラムを複数回実行しても常に同じ乱数シーケンスが生成されることを意味します。開発者がこの挙動を理解していない場合、テストやデバッグの際には便利ですが、本番環境で予測不可能な乱数が必要なアプリケーション(例:ゲーム、シミュレーション、セキュリティ関連)では問題となります。

Issue #5937では、この決定論的な挙動がドキュメントで十分に強調されていないという指摘がありました。特に、rand.Intn() のようなトップレベル関数を使用する際に、rand.Seed() を呼び出して乱数ジェネレータを初期化する必要があることが明確ではありませんでした。このコミットは、この混乱を解消し、ユーザーが math/rand パッケージをより安全かつ意図通りに使用できるようにするためのドキュメンテーション改善を目的としています。

前提知識の解説

擬似乱数生成器 (PRNG)

コンピュータで生成される乱数は、実際には「擬似乱数」と呼ばれます。これは、ある初期値(シード)から決定論的なアルゴリズムによって生成されるためです。同じシードを与えれば、常に同じ乱数シーケンスが生成されます。

シード (Seed)

擬似乱数生成器の初期状態を決定する値です。シードが異なれば、生成される乱数シーケンスも異なります。予測不可能な乱数が必要な場合は、time.Now().UnixNano() のような、実行ごとに異なる値(例:現在の時刻のナノ秒単位のタイムスタンプ)をシードとして使用するのが一般的です。

Go言語の math/rand パッケージ

Goの math/rand パッケージは、擬似乱数を生成するための機能を提供します。

  • rand.Source インターフェース: 乱数生成の基となるソースを定義するインターフェースです。
  • rand.NewSource(seed int64): 指定されたシード値で新しい Source を作成します。
  • rand.Rand 構造体: Source をラップし、様々な型の乱数を生成するためのメソッド(Intn, Float64 など)を提供します。
  • トップレベル関数: rand.Intn(), rand.Float64() など、パッケージ名で直接呼び出せる関数群です。これらは内部的にグローバルな *Rand インスタンス(デフォルトの共有 Source を使用)を利用します。
  • rand.Seed(seed int64): グローバルな乱数ジェネレータのシードを設定します。この関数を呼び出さない場合、デフォルトのシード値 1 が使用されます。

Example 関数と go test

Goのテストフレームワークでは、Example 関数という特別な関数を記述することで、コードの実行例をドキュメントとして提供できます。これらの関数は go test コマンドで実行され、出力が期待される出力と一致するかどうかが検証されます。これにより、ドキュメントの正確性が保証されます。

技術的詳細

このコミットは主に src/pkg/math/rand/example_test.gosrc/pkg/math/rand/rand.go の2つのファイルに変更を加えています。

src/pkg/math/rand/rand.go の変更点

  1. パッケージ概要の更新: Package rand implements pseudo-random number generators. の後に、以下の説明が追加されました。

    // Random numbers are generated by a Source. Top-level functions, such as
    // Float64 and Int, use a default shared Source that produces a deterministic
    // sequence of values each time a program is run. Use the Seed function to
    // initialize the default Source if different behavior is required for each run.
    

    この変更により、トップレベル関数がデフォルトの共有 Source を使用し、その結果が決定論的であること、そして異なる挙動が必要な場合に Seed 関数を使用する必要があることが明確に説明されています。これは、Issue #5937で指摘されたドキュメントの不足を直接的に解消するものです。

  2. Seed 関数のコメント更新: Seed 関数のコメントが Seed uses the provided seed value to initialize the generator to a から Seed uses the provided seed value to initialize the default Source to a に変更されました。これにより、Seed がグローバルなジェネレータではなく、「デフォルトの Source」を初期化することがより正確に表現されています。

  3. トップレベル関数のコメント更新: Int63, Uint32, Int31, Int, Int63n, Int31n, Intn, Float64, Float32, Perm, NormFloat64, ExpFloat64 といったトップレベルの乱数生成関数のコメントに、一貫して「from the default Source.」という記述が追加されました。 例:

    • Int63 returns a non-negative pseudo-random 63-bit integer as an int64.Int63 returns a non-negative pseudo-random 63-bit integer as an int64\n// from the default Source. この変更は、これらの関数が内部的にグローバルな(デフォルトの)乱数ソースを使用していることを明示し、ユーザーが Seed の影響範囲を理解しやすくすることを目的としています。

src/pkg/math/rand/example_test.go の変更点

  1. 既存の Example 関数のリネーム: 既存の func Example() { ... }func Example_rand() { ... } にリネームされました。これは、新しいトップレベルの Example() 関数を追加するために、名前の衝突を避けるためです。Goのテストフレームワークでは、Example 関数はパッケージのドキュメントに表示され、Example_Suffix の形式で複数の例を区別できます。

  2. 新しいトップレベル Example() 関数の追加: 以下の新しい Example() 関数が追加されました。

    func Example() {
    	rand.Seed(42) // Try changing this number!
    	answers := []string{
    		"It is certain",
    		"It is decidedly so",
    		// ... (省略) ...
    		"Very doubtful",
    	}
    	fmt.Println("Magic 8-Ball says:", answers[rand.Intn(len(answers))])
    	// Output: Magic 8-Ball says: As I see it yes
    }
    

    この新しい例は、rand.Seed(42) を明示的に呼び出すことで、グローバルな乱数ジェネレータのシードを設定する方法を示しています。これにより、rand.Intn の結果が決定論的になることをユーザーに示し、「この数字を変えてみてください!」というコメントで、シードを変更することの重要性を強調しています。Magic 8-Ballの例は、乱数生成の具体的なユースケースを分かりやすく提示しています。// Output: コメントは、go test がこの例の出力を検証するために使用されます。

これらの変更は、math/rand パッケージのドキュメンテーションを改善し、ユーザーが乱数生成の決定論的な性質と Seed 関数の役割をより明確に理解できるようにすることを目的としています。

コアとなるコードの変更箇所

src/pkg/math/rand/example_test.go

--- a/src/pkg/math/rand/example_test.go
+++ b/src/pkg/math/rand/example_test.go
@@ -11,12 +11,40 @@ import (
 	"text/tabwriter"
 )
 
-// This test serves as an example but also makes sure we don't change
+// These tests serve as an example but also make sure we don't change
 // the output of the random number generator when given a fixed seed.
 
+func Example() {
+	rand.Seed(42) // Try changing this number!
+	answers := []string{
+		"It is certain",
+		"It is decidedly so",
+		"Without a doubt",
+		"Yes definitely",
+		"You may rely on it",
+		"As I see it yes",
+		"Most likely",
+		"Outlook good",
+		"Yes",
+		"Signs point to yes",
+		"Reply hazy try again",
+		"Ask again later",
+		"Better not tell you now",
+		"Cannot predict now",
+		"Concentrate and ask again",
+		"Don't count on it",
+		"My reply is no",
+		"My sources say no",
+		"Outlook not so good",
+		"Very doubtful",
+	}
+	fmt.Println("Magic 8-Ball says:", answers[rand.Intn(len(answers))])
+	// Output: Magic 8-Ball says: As I see it yes
+}
+
 // This example shows the use of each of the methods on a *Rand.
 // The use of the global functions is the same, without the receiver.
-func Example() {
+func Example_rand() {
 	// Create and seed the generator.
 	// Typically a non-fixed seed should be used, such as time.Now().UnixNano().
 	// Using a fixed seed will produce the same output on every run.

src/pkg/math/rand/rand.go

--- a/src/pkg/math/rand/rand.go
+++ b/src/pkg/math/rand/rand.go
@@ -3,6 +3,11 @@
 // license that can be found in the LICENSE file.
 
 // Package rand implements pseudo-random number generators.
+//
+// Random numbers are generated by a Source. Top-level functions, such as
+// Float64 and Int, use a default shared Source that produces a deterministic
+// sequence of values each time a program is run. Use the Seed function to
+// initialize the default Source if different behavior is required for each run.
 package rand
 
 import "sync"
@@ -113,47 +118,57 @@ func (r *Rand) Perm(n int) []int {
 
 var globalRand = New(&lockedSource{src: NewSource(1)})
 
-// Seed uses the provided seed value to initialize the generator to a
+// Seed uses the provided seed value to initialize the default Source to a
 // deterministic state. If Seed is not called, the generator behaves as
 // if seeded by Seed(1).
 func Seed(seed int64) { globalRand.Seed(seed) }
 
-// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.\n
+// Int63 returns a non-negative pseudo-random 63-bit integer as an int64\n
+// from the default Source.
 func Int63() int64 { return globalRand.Int63() }
 
-// Uint32 returns a pseudo-random 32-bit value as a uint32.\n
+// Uint32 returns a pseudo-random 32-bit value as a uint32\n
+// from the default Source.
 func Uint32() uint32 { return globalRand.Uint32() }
 
-// Int31 returns a non-negative pseudo-random 31-bit integer as an int32.\n
+// Int31 returns a non-negative pseudo-random 31-bit integer as an int32\n
+// from the default Source.
 func Int31() int32 { return globalRand.Int31() }
 
-// Int returns a non-negative pseudo-random int.\n
+// Int returns a non-negative pseudo-random int from the default Source.
 func Int() int { return globalRand.Int() }
 
-// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).\n
+// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n)\n
+// from the default Source.
 // It panics if n <= 0.
 func Int63n(n int64) int64 { return globalRand.Int63n(n) }
 
-// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).\n
+// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n)\n
+// from the default Source.
 // It panics if n <= 0.
 func Int31n(n int32) int32 { return globalRand.Int31n(n) }
 
-// Intn returns, as an int, a non-negative pseudo-random number in [0,n).\n
+// Intn returns, as an int, a non-negative pseudo-random number in [0,n)\n
+// from the default Source.
 // It panics if n <= 0.
 func Intn(n int) int { return globalRand.Intn(n) }
 
-// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).\n
+// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0)\n
+// from the default Source.
 func Float64() float64 { return globalRand.Float64() }
 
-// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0).\n
+// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0)\n
+// from the default Source.
 func Float32() float32 { return globalRand.Float32() }
 
-// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).\n
+// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n)\n
+// from the default Source.
 func Perm(n int) []int { return globalRand.Perm(n) }
 
 // NormFloat64 returns a normally distributed float64 in the range
 // [-math.MaxFloat64, +math.MaxFloat64] with
-// standard normal distribution (mean = 0, stddev = 1).\n
+// standard normal distribution (mean = 0, stddev = 1)\n
+// from the default Source.
 // To produce a different normal distribution, callers can
 // adjust the output using:
 //
@@ -163,7 +178,7 @@ func NormFloat64() float64 { return globalRand.NormFloat64() }
 
 // ExpFloat64 returns an exponentially distributed float64 in the range
 // (0, +math.MaxFloat64] with an exponential distribution whose rate parameter
-// (lambda) is 1 and whose mean is 1/lambda (1).\n
+// (lambda) is 1 and whose mean is 1/lambda (1) from the default Source.
 // To produce a distribution with a different rate parameter,
 // callers can adjust the output using:
 //

コアとなるコードの解説

src/pkg/math/rand/example_test.go の変更

  • Example() の追加: この新しい関数は、rand.Seed(42) を呼び出してグローバルな乱数ジェネレータを初期化する方法を具体的に示しています。これにより、rand.Intn の結果がシード値によって決定されることをユーザーが体験できます。Magic 8-Ballの例は、乱数生成の一般的なユースケースを分かりやすく提示し、ドキュメントとしての価値を高めています。// Output: コメントは、go test がこの例の出力を検証するために使用され、ドキュメントの正確性を保証します。
  • Example() から Example_rand() へのリネーム: 既存の Example() 関数を Example_rand() にリネームすることで、新しいトップレベルの Example() 関数との名前の衝突を避け、Goのドキュメンテーションツールが両方の例を適切に表示できるようにしています。

src/pkg/math/rand/rand.go の変更

  • パッケージ概要の拡張: math/rand パッケージの冒頭に、トップレベル関数がデフォルトの共有 Source を使用し、その結果が決定論的であること、そして異なる挙動が必要な場合に Seed 関数を使用する必要があることを明確に説明するコメントが追加されました。これは、ユーザーが math/rand パッケージの基本的な挙動を誤解するのを防ぐための重要な改善です。
  • Seed 関数のコメントの明確化: Seed 関数が「デフォルトの Source」を初期化することを明記することで、その役割と影響範囲をより正確に伝えています。
  • トップレベル関数のコメントの統一: Int63, Intn, Float64 など、すべてのトップレベル乱数生成関数のコメントに「from the default Source.」というフレーズが追加されました。これにより、これらの関数がグローバルな(デフォルトの)乱数ソースに依存していることが一貫して示され、ユーザーが Seed の影響範囲をより深く理解できるようになります。

これらの変更は、コードの機能自体を変更するものではなく、主にドキュメンテーションの改善と例の追加を通じて、math/rand パッケージの使いやすさと理解度を向上させることを目的としています。特に、乱数生成の決定論的な性質と Seed 関数の重要性について、ユーザーがより明確なガイダンスを得られるようになりました。

関連リンク

参考にした情報源リンク