У вас есть несколько вариантов здесь:
- биг-массивы
- массивы
- Карты
Bigarrays может иметь только цифры, как их значения, но они могут быть произвольные размеры и размеры. Они также имеют приятный интерфейс для доступа к многомерным данным. Массивы могут иметь значения любого типа, и вы можете создавать массивы массивов для моделирования многомерных пространств. Оба массива и bigarrays являются обязательными структурами данных, так что это будет ваш код. Вот почему я предлагаю вам использовать постоянные карты, чтобы сделать ваш код чисто функциональным (если вы хотите практиковать в 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
функцию, которая будет создавать 2d массива заданных размеров и заполнить его с значением параметра, например,
make_matrix 300 400 0
создаст нулевой заполненную матрицу с 300 строк и 400 столбцов.
В нашем случае мы не хотим заполнять матрицу числами, но с состояниями.Нам понадобится тип для состояния, который может представлять 3 состояния, мы будем повторно использовать тип state
из предыдущего примера, но мы также переносим его в тип option
, чтобы была представлена или мертвая или живая ячейка в Some Dead
или Some Live
, а безлюдный один будет просто None
, так что мы можем создать пустую доску с
Array.make_matrix width height None
чтобы инициализировать нашу доску можно сначала создать, а затем дать жизнь случайно выбранные клетки на основе требуемой плотности населения. Мы будем представлять плотность с числом с плавающей запятой между нулем и одним, например, плотность 0.1
означает, что примерно 10% ячеек будут жить. Чтобы оставаться более функциональным, мы будем использовать Array.map
для преобразования наших массивов. Явные циклы и итерации с INPLACE модификаций будет быстрее и более идиоматических конечно, но только ради эксперимента мы будем использовать более функциональный подход:
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
функция называет при условии пользовательской функции с индексом элемента , В нашем случае вероятность жизни не зависит от координат, поэтому мы можем просто игнорировать позицию, поэтому мы использовали _
для обозначения того, что мы не будем использовать аргумент.
Спасибо за ответ, я решил пойти с матрицей, и знаете ли вы, может ли этот способ заполнить матрицу живыми или мертвыми клетками на основе определенной плотности населения? Я использую плотность ширины ширины make_matrix –
Хорошо, я обновил ответ с помощью массива. Счастливая верховая езда :) – ivg