這一篇承接 Day 11 的 mongory_value,筆者帶讀者走一遍 C core 裡的動態陣列 mongory_array:為什麼長這樣、怎麼用、有哪些邊界與測試重點
整體 API 以 Ruby 習慣為靈感:push/get/set/each/sort_by/includes,同時確保擴容策略簡潔、可觀測、易於與 Matcher 串接。
pool->error,便於測試/回報。mongory_value 深度整合:比對/字串化皆走一致入口。mongory_array_new(pool):建立新陣列(初始容量 4)。array->push(array, value):尾端加入元素;自動擴容(倍增)。array->get(array, index):取得元素;越界回傳 NULL。array->set(array, index, value):設定元素;若跨越目前長度,會用 NULL 補齊中間空洞並更新 count。array->each(array, acc, callback):逐一走訪;callback(item, acc) 回傳 false 可提前停止。mongory_array_sort_by(array, temp_pool, ctx, callback):依 key 排序,回傳新陣列;callback(value, ctx) -> size_t key。mongory_array_includes(array, value):是否包含;比較走 mongory_value->comp。對照 Ruby:
arr.push(x) → C array->push(array, x)
arr[i] → C array->get(array, i);Ruby arr[i] = x → C array->set(array, i, x)
arr.each { |x| } → C array->each(array, acc, cb)
arr.sort_by { |x| key } → C mongory_array_sort_by(array, tmp, ctx, cb)
以下展示 push/get/each/sort_by 的基本用法(以整數為例):
#include <mongory-core/foundations/memory_pool.h>
#include <mongory-core/foundations/array.h>
#include <mongory-core/foundations/value.h>
#include <stdio.h>
static bool print_item(mongory_value *item, void *acc) {
mongory_memory_pool *pool = (mongory_memory_pool *)acc;
char *s = item->to_str(item, pool);
printf("%s\n", s);
return true;
}
static size_t key_of(mongory_value *item, void *ctx) {
// 以整數值作為排序 key(小到大)
(void)ctx;
return (size_t)item->data.i64;
}
int main() {
mongory_init();
mongory_memory_pool *pool = mongory_memory_pool_new();
mongory_array *array = mongory_array_new(pool);
array->push(array, mongory_value_wrap_int(pool, 3));
array->push(array, mongory_value_wrap_int(pool, 1));
array->push(array, mongory_value_wrap_int(pool, 2));
// get / set
mongory_value *v1 = array->get(array, 1); // 1
printf("get[1]=%lld\n", v1->data.i64);
array->set(array, 5, mongory_value_wrap_int(pool, 9)); // 自動補 NULL,count 變為 6
// each(列印)
array->each(array, pool, print_item);
// sort_by(回傳新陣列,不就地排序)
mongory_array *sorted = mongory_array_sort_by(array, pool, NULL, key_of);
printf("-- sorted --\n");
sorted->each(sorted, pool, print_item);
mongory_memory_pool_free(pool);
mongory_cleanup();
return 0;
}
push 攤銷 O(1),擴容發生時計 O(n) 搬移指標。set 若直接跳到遠端索引,會補 NULL 形成「洞洞」;count 會被推進到 index+1。get 越界回 NULL;呼叫端需判斷。set 造成的 NULL 空洞會影響部分操作:
includes 內部直接取出元素後呼叫 item->comp;若該位置為 NULL 將不安全。建議在有空洞情境先避免 includes,或先以實值補齊。each 會把 NULL 交給回呼;回呼需自保(可檢查 item == NULL)。sort_by 使用合併排序實作,會在 temp_pool 產生中介陣列;是否穩定排序取決於 callback 與實作,不保證穩定。pool->error(集中回報)。$every、$elemMatch 等操作會頻繁走 array->each;以「方法掛在物件」的風格,可讀性更接近 Ruby。get/set 的語意需簡單直覺、邊界明確。sort_by 與 includes 可支援測試/診斷用例(例如 explain/trace 的輸出排序一致性)。new 後容量/計數與函式指標皆正確。get 回 NULL;跨距 set 形成空洞,count 正確。false 可提前停止;能處理 NULL。temp_pool 釋放後不影響原陣列。pool->error 被寫入。mongory_array 提供 Ruby 風 API 與可預期的擴容行為,讓上層 Matchers(特別是 $every/$elemMatch)易於組合與觀測。Table(雜湊、碰撞與 rehash 取捨),完成基礎容器三件組(Value/Array/Table)。