我正在努力做康威的练习游戏,因为我之前在C ++中已经完成了这个,但我想知道如何在Ocaml中根据随机分配的spawns给出高度和宽度来生成二维数组给定的人口密度,我试图使用make_matrix。我在网上找到的所有教程都使用图形或其他一些硬编码方式(即rosetta代码),但我想尽量避免这种情况,以便我可以有一些变化。谢谢。
这是我当前的代码,它返回未绑定的值。
print_string "Input width ";
let num = read_int () in
print_string "Input height";
let num2 = read_int () in
myArray = Array.make_matrix num num2 0;
error: Unbound value myArray;
答案 0 :(得分:1)
您有多种选择:
Bigarrays只能将数字作为其值,但它们可以是任意大小和尺寸。它们还具有用于访问多维数据的良好界面。数组可以包含任何类型的值,您可以创建数组数组来建模多维空间。数组和bigarray都是命令式数据结构,因此代码也是如此。这就是为什么我建议你使用持久性地图,使你的代码纯功能(如果你想在OCaml中练习,最好在其功能子空间中练习)。
因此,让我们为状态和坐标定义一个类型:
type state = Dead | Live
type coord = {x : int; y : int}
由于我们将使用地图,因此我们不需要无人居住的状态。未填充的状态只是未映射的。
现在,我们可以定义电路板数据结构的实现:
module Board = Map.Make(struct
type t = coord
let compare = compare
end)
作为使用示例,让我们定义一个名为fold_neighbors
的更高阶函数,它将用户提供的函数应用于每个填充的相邻单元格。
let neighbors {x;y} = [
x, y+1;
x+1,y+1;
x+1,y;
x+1,y-1;
x, y-1;
x-1,y-1;
x-1,y;
x-1,y+1;
] |> List.map (fun (x,y) -> {x;y})
let fold_neighbors board cell ~f ~init =
neighbors cell |>
List.fold_left (fun acc n ->
try f acc (Board.find n board)
with Not_found -> acc)
init
使用这个通用迭代器函数,我们可以定义像count_live_neighbors
这样的专用函数:
let count_live_neighbors =
fold_neighbors ~init:0 ~f:(fun count nb -> match nb with
| Live -> count + 1
| Dead -> count)
实现假设一个无限的板,如果你想使它有界,那么你需要调整fold_neighbors
函数来排除那些在板外面的人。
另一种选择是使用普通数组。我们可以使用方便的make_matrix
函数创建给定大小的二维数组,并用提供的值填充它,例如
make_matrix 300 400 0
将创建一个零填充矩阵,包含300行和400列。
在我们的案例中,我们不想用数字填充矩阵,而是用状态填充矩阵。我们需要一个状态类型,它能够代表3个状态,我们将重用上一个例子中的state
类型,但我们也将它包装成option
类型,以便一个单元格无论是死的还是现场的都会被表示为Some Dead
或Some Live
,而未填充的只会None
,所以我们可以创建一个空板
Array.make_matrix width height None
为了初始化我们的电路板,我们可以先创建它,然后根据所需的人口密度为随机选择的细胞提供生命。我们将用0到1之间的浮点数来表示密度,例如,密度0.1
意味着大约10%的单元将是活的。为了保持更多功能,我们将使用Array.map
来转换我们的数组。当然,使用就地修改的显式循环和迭代会更快,更惯用,但仅仅为了实验,我们可以使用更多功能方法:
let create_board width height density =
Array.make_matrix width height None |>
Array.map (Array.map (fun cell ->
if Random.float 1.0 > density then Some Live else None))
但是,我们可以更好地完成这项工作,如果我们不会在第一手创建空板,而是从初始化的板开始。为此,我们可以使用Array.init
函数,它允许为每个单元格提供不同的值,这是更好的create_board
函数的示例:
let create_board width height density =
Array.init height (fun _ ->
Array.init width (fun _ ->
if Random.float 1.0 > density then Some Live else None))
Array.init
函数使用元素的索引调用用户提供的函数。在我们的例子中,生命的概率并不取决于坐标,所以我们可以忽略这个位置,这就是我们使用_
指定的原因,我们不会去使用参数。