Skip to content

Commit 37e4730

Browse files
authored
[215_18] 实现 (liii random) 和 (srfi srfi-27) 随机数模块 (#666)
1 parent 00445d7 commit 37e4730

16 files changed

+1055
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ result*
3232
tests/resources/hashlib-test-large-local.txt
3333
tests/function-library-index.json
3434
.claude/settings.local.json
35+
.codex

devel/215_18.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# [215_18] 实现 (liii random) 模块
2+
3+
## 任务相关的代码文件
4+
- `goldfish/liii/random.scm` - (liii random) 导出模块
5+
- `goldfish/srfi/srfi-27.scm` - SRFI-27 实现
6+
- `tests/goldfish/liii/random-test.scm` - 测试文件
7+
8+
## 如何测试
9+
```bash
10+
# 1. 构建
11+
xmake b goldfish
12+
13+
# 2. 格式化代码
14+
bin/gf fix goldfish/liii/random.scm
15+
bin/gf fix goldfish/srfi/srfi-27.scm
16+
bin/gf fix tests/goldfish/liii/random-test.scm
17+
18+
# 3. 运行测试
19+
bin/gf tests/goldfish/liii/random-test.scm
20+
```
21+
22+
## 2025-04-03 实现 (liii random) 模块
23+
24+
### What
25+
实现基于 SRFI-27 的随机数生成模块,包括:
26+
27+
1. **创建文件 `goldfish/liii/random.scm`**
28+
- 作为导出模块,导入并重导出 (srfi srfi-27) 的所有功能
29+
30+
2. **创建文件 `goldfish/srfi/srfi-27.scm`**
31+
- 基于 s7.c 内置的 random 函数实现 SRFI-27 规范
32+
- 使用 Sebastian Egner 的版权和许可证(SRFI-27 原始版权)
33+
- 实现 random-source 记录类型
34+
- 实现 SRFI-27 标准函数
35+
36+
3. **创建测试文件 `tests/liii/random-test.scm`**
37+
- 作为函数分类索引和文档说明
38+
39+
4. **创建单个函数测试文件 `tests/liii/random/` 目录**
40+
- `random-integer-test.scm` - random-integer 测试
41+
- `random-real-test.scm` - random-real 测试
42+
- `make-random-source-test.scm` - make-random-source 测试
43+
- `random-source-p-test.scm` - random-source? 测试
44+
- `random-source-state-ref-test.scm` - random-source-state-ref 测试
45+
- `random-source-state-set-test.scm` - random-source-state-set! 测试
46+
- `random-source-randomize-test.scm` - random-source-randomize! 测试
47+
- `random-source-pseudo-randomize-test.scm` - random-source-pseudo-randomize! 测试
48+
- `random-source-make-integers-test.scm` - random-source-make-integers 测试
49+
- `random-source-make-reals-test.scm` - random-source-make-reals 测试
50+
- `reproducibility-test.scm` - 可复现性测试
51+
52+
### Why
53+
Goldfish Scheme 需要标准的随机数生成接口,SRFI-27 是 Scheme 社区广泛接受的标准。
54+
s7.c 内置了 `random` 函数和 `random-state` 类型,基于这些内置功能实现 SRFI-27 可以:
55+
1. 利用 s7.c 的高性能随机数生成器
56+
2. 提供符合标准 SRFI-27 的接口
57+
3. 保持与现有 Scheme 生态系统的兼容性
58+
59+
### How
60+
61+
#### s7.c 内置功能
62+
- `(random n)` - 返回 [0, n) 范围内的随机数
63+
- `(random n state)` - 使用指定的 random-state
64+
- `(random-state seed)` - 创建新的 random-state
65+
- `(random-state? obj)` - 检查是否为 random-state
66+
- `(random-state->list state)` - 将状态转换为列表 `(seed carry)`
67+
68+
#### SRFI-27 与 s7.c 的映射
69+
SRFI-27 的 random-source 是一个记录类型,包含:
70+
- state-ref: 返回状态
71+
- state-set!: 设置状态
72+
- randomize!: 用时间随机化
73+
- pseudo-randomize!: 用索引伪随机化
74+
- make-integers: 创建整数生成器
75+
- make-reals: 创建实数生成器
76+
77+
状态格式:s7.c 的 random-state 可以用 `(random-state->list state)` 获取为 `(seed carry)` 列表
78+
79+
#### 实现要点
80+
1. 使用 `define-record-type` 定义 random-source 类型
81+
2. 使用 s7.c 的 `(random-state seed)` 创建新的状态
82+
3. 使用 `(random-state->list state)` 获取和设置状态
83+
4. 使用 `(random n state)` 生成随机数
84+
5. 用当前时间(纳秒)作为 `random-source-randomize!` 的熵源

goldfish/liii/random.scm

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
;
2+
; Copyright (C) 2025 The Goldfish Scheme Authors
3+
;
4+
; Licensed under the Apache License, Version 2.0 (the "License");
5+
; you may not use this file except in compliance with the License.
6+
; You may obtain a copy of the License at
7+
;
8+
; http://www.apache.org/licenses/LICENSE-2.0
9+
;
10+
; Unless required by applicable law or agreed to in writing, software
11+
; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
; License for the specific language governing permissions and limitations
14+
; under the License.
15+
16+
;;
17+
;; (liii random) - Random number generation
18+
;;
19+
;; This module exports the SRFI-27 interface for random number generation.
20+
;; It re-exports all symbols from (srfi srfi-27).
21+
;;
22+
;; Exported functions:
23+
;; random-integer - Generate random integer in [0, n-1]
24+
;; random-real - Generate random real in (0, 1)
25+
;; default-random-source - The default random source
26+
;; make-random-source - Create a new random source
27+
;; random-source? - Check if object is a random source
28+
;; random-source-state-ref - Get the state of a random source
29+
;; random-source-state-set!- Set the state of a random source
30+
;; random-source-randomize!- Randomize a source with current time
31+
;; random-source-pseudo-randomize! - Deterministically set source state
32+
;; random-source-make-integers - Create integer generator from source
33+
;; random-source-make-reals - Create real generator from source
34+
35+
(define-library (liii random)
36+
(import (srfi srfi-27))
37+
(export
38+
random-integer
39+
random-real
40+
default-random-source
41+
make-random-source
42+
random-source?
43+
random-source-state-ref
44+
random-source-state-set!
45+
random-source-randomize!
46+
random-source-pseudo-randomize!
47+
random-source-make-integers
48+
random-source-make-reals
49+
) ;export
50+
) ;define-library

goldfish/srfi/srfi-27.scm

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
;; SRFI-27 Implementation for Goldfish Scheme
2+
;;
3+
;; This is an implementation of SRFI-27 "Sources of Random Bits".
4+
;; It is based on s7.c's built-in random functions.
5+
;;
6+
;; Copyright (C) Sebastian Egner (2002). All Rights Reserved.
7+
;;
8+
;; Permission is hereby granted, free of charge, to any person obtaining
9+
;; a copy of this software and associated documentation files (the
10+
;; "Software"), to deal in the Software without restriction, including
11+
;; without limitation the rights to use, copy, modify, merge, publish,
12+
;; distribute, sublicense, and/or sell copies of the Software, and to
13+
;; permit persons to whom the Software is furnished to do so, subject to
14+
;; the following conditions:
15+
;;
16+
;; The above copyright notice and this permission notice shall be
17+
;; included in all copies or substantial portions of the Software.
18+
;;
19+
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21+
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22+
;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23+
;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24+
;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25+
;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
27+
(define-library (srfi srfi-27)
28+
(import (scheme base)
29+
(srfi srfi-19)
30+
(liii error)
31+
) ;import
32+
(export
33+
random-integer
34+
random-real
35+
default-random-source
36+
make-random-source
37+
random-source?
38+
random-source-state-ref
39+
random-source-state-set!
40+
random-source-randomize!
41+
random-source-pseudo-randomize!
42+
random-source-make-integers
43+
random-source-make-reals
44+
) ;export
45+
(begin
46+
47+
;; ====================
48+
;; Random Source Record Type
49+
;; ====================
50+
;; A random-source is a record containing:
51+
;; - state: the underlying s7 random-state object
52+
;; - state-ref: thunk to get the state as external representation
53+
;; - state-set!: procedure to set state from external representation
54+
;; - randomize!: procedure to randomize state
55+
;; - pseudo-randomize!: procedure to pseudo-randomize with indices
56+
;; - make-integers: procedure returning a random-integer generator
57+
;; - make-reals: procedure returning a random-real generator
58+
59+
(define-record-type <random-source>
60+
(%make-random-source state state-ref state-set! randomize! pseudo-randomize! make-integers make-reals)
61+
random-source?
62+
(state random-source-internal-state)
63+
(state-ref random-source-state-ref-proc)
64+
(state-set! random-source-state-set-proc)
65+
(randomize! random-source-randomize-proc)
66+
(pseudo-randomize! random-source-pseudo-randomize-proc)
67+
(make-integers random-source-make-integers-proc)
68+
(make-reals random-source-make-reals-proc)
69+
) ;define-record-type
70+
71+
;; ====================
72+
;; Internal Helpers
73+
;; ====================
74+
75+
;; Get current time in nanoseconds as integer
76+
;; Used for randomization
77+
(define (current-time-nanoseconds)
78+
(let ((t (current-time TIME-UTC)))
79+
(+ (* (time-second t) 1000000000)
80+
(time-nanosecond t)
81+
) ;+
82+
) ;let
83+
) ;define
84+
85+
;; Create a new s7 random-state with given seed and carry
86+
(define (make-s7-random-state seed carry)
87+
(random-state seed carry)
88+
) ;define
89+
90+
;; Get state as list (seed carry)
91+
(define (get-s7-state state)
92+
(random-state->list state)
93+
) ;define
94+
95+
;; ====================
96+
;; Random Source Operations
97+
;; ====================
98+
99+
(define (make-random-source)
100+
(let ((state (random-state 0))) ; Create initial state with seed 0
101+
(%make-random-source
102+
state
103+
;; state-ref: return external representation
104+
(lambda ()
105+
(cons 'random-source-state (random-state->list state))
106+
) ;lambda
107+
;; state-set!: set state from external representation
108+
(lambda (new-state)
109+
(unless (and (pair? new-state)
110+
(eq? (car new-state) 'random-source-state)
111+
(= (length new-state) 3))
112+
(error 'wrong-type-arg "invalid random source state" new-state)
113+
) ;unless
114+
(let ((seed (cadr new-state))
115+
(carry (caddr new-state)))
116+
(set! state (random-state seed carry))
117+
) ;let
118+
) ;lambda
119+
;; randomize!: use current time to randomize
120+
(lambda ()
121+
(let ((ns (current-time-nanoseconds)))
122+
;; Use nanoseconds to create a pseudo-random seed
123+
(let ((seed (modulo ns 4294967296))
124+
(carry (modulo (quotient ns 4294967296) 4294967296)))
125+
(set! state (random-state seed carry))
126+
) ;let
127+
) ;let
128+
) ;lambda
129+
;; pseudo-randomize!: use i, j indices
130+
(lambda (i j)
131+
(unless (and (integer? i) (exact? i) (>= i 0))
132+
(error 'wrong-type-arg "pseudo-randomize! i must be a non-negative exact integer" i)
133+
) ;unless
134+
(unless (and (integer? j) (exact? j) (>= j 0))
135+
(error 'wrong-type-arg "pseudo-randomize! j must be a non-negative exact integer" j)
136+
) ;unless
137+
;; Create a deterministic state based on i and j
138+
;; Using a simple hash of i and j to create seed and carry
139+
(let ((seed (modulo (+ (* i 12345) j) 4294967296))
140+
(carry (modulo (+ (* j 54321) i) 4294967296)))
141+
(set! state (random-state seed carry))
142+
) ;let
143+
) ;lambda
144+
;; make-integers: return a procedure that generates random integers
145+
(lambda ()
146+
(lambda (n)
147+
(unless (and (integer? n) (exact? n) (positive? n))
148+
(error 'wrong-type-arg "random-integer: n must be a positive exact integer" n)
149+
) ;unless
150+
;; s7's random returns [0, n), we need [0, n-1] which is the same
151+
(random n state)
152+
) ;lambda
153+
) ;lambda
154+
;; make-reals: return a procedure that generates random reals
155+
(lambda args
156+
(let ((unit #f))
157+
(if (pair? args)
158+
(begin
159+
(set! unit (car args))
160+
(unless (and (real? unit) (< 0 unit 1))
161+
(error 'wrong-type-arg "random-source-make-reals: unit must be a real in (0,1)" unit)
162+
) ;unless
163+
) ;begin
164+
) ;if
165+
(lambda ()
166+
(let ((r (random 1.0 state)))
167+
;; random returns [0.0, 1.0), but SRFI-27 requires (0, 1)
168+
;; s7's random for reals already returns (0, 1) when n > 0
169+
;; But we need to ensure we never return 0 or 1
170+
(if (zero? r)
171+
0.0000000000000001 ; smallest positive value
172+
r
173+
) ;if
174+
) ;let
175+
) ;lambda
176+
) ;let
177+
) ;lambda
178+
) ;%make-random-source
179+
) ;let
180+
) ;define
181+
182+
;; ====================
183+
;; Standard Interface
184+
;; ====================
185+
186+
(define default-random-source (make-random-source))
187+
188+
(define (random-integer n)
189+
((random-source-make-integers default-random-source) n)
190+
) ;define
191+
192+
(define (random-real)
193+
((random-source-make-reals default-random-source))
194+
) ;define
195+
196+
;; ====================
197+
;; Random Source State Operations
198+
;; ====================
199+
200+
(define (random-source-state-ref s)
201+
(unless (random-source? s)
202+
(error 'wrong-type-arg "random-source-state-ref: expected random-source" s)
203+
) ;unless
204+
((random-source-state-ref-proc s))
205+
) ;define
206+
207+
(define (random-source-state-set! s new-state)
208+
(unless (random-source? s)
209+
(error 'wrong-type-arg "random-source-state-set!: expected random-source" s)
210+
) ;unless
211+
((random-source-state-set-proc s) new-state)
212+
) ;define
213+
214+
(define (random-source-randomize! s)
215+
(unless (random-source? s)
216+
(error 'wrong-type-arg "random-source-randomize!: expected random-source" s)
217+
) ;unless
218+
((random-source-randomize-proc s))
219+
) ;define
220+
221+
(define (random-source-pseudo-randomize! s i j)
222+
(unless (random-source? s)
223+
(error 'wrong-type-arg "random-source-pseudo-randomize!: expected random-source" s)
224+
) ;unless
225+
((random-source-pseudo-randomize-proc s) i j)
226+
) ;define
227+
228+
;; ====================
229+
;; Random Source Generator Creation
230+
;; ====================
231+
232+
(define (random-source-make-integers s)
233+
(unless (random-source? s)
234+
(error 'wrong-type-arg "random-source-make-integers: expected random-source" s)
235+
) ;unless
236+
((random-source-make-integers-proc s))
237+
) ;define
238+
239+
(define (random-source-make-reals s . unit)
240+
(unless (random-source? s)
241+
(error 'wrong-type-arg "random-source-make-reals: expected random-source" s)
242+
) ;unless
243+
(apply (random-source-make-reals-proc s) unit)
244+
) ;define
245+
246+
) ;begin
247+
) ;define-library

0 commit comments

Comments
 (0)