From 79fa51c5d2db84cf2fe0cdf986fafd0cc61a876d Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Mon, 16 Dec 2024 10:16:22 +0000 Subject: [PATCH] Add array manipulation functions (#629) Co-authored-by: Maksym Hazevych --- src/std/array.ab | 52 +++++++++++++++++++++++++++ src/tests/stdlib/array_extract_at.ab | 25 +++++++++++++ src/tests/stdlib/array_first.ab | 17 +++++++++ src/tests/stdlib/array_first_index.ab | 22 +++++++++--- src/tests/stdlib/array_last.ab | 17 +++++++++ src/tests/stdlib/array_pop.ab | 17 +++++++++ src/tests/stdlib/array_remove_at.ab | 25 +++++++++++++ src/tests/stdlib/array_remove_each.ab | 21 +++++++++++ src/tests/stdlib/array_search.ab | 23 +++++++++--- src/tests/stdlib/array_shift.ab | 17 +++++++++ 10 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 src/tests/stdlib/array_extract_at.ab create mode 100644 src/tests/stdlib/array_first.ab create mode 100644 src/tests/stdlib/array_last.ab create mode 100644 src/tests/stdlib/array_pop.ab create mode 100644 src/tests/stdlib/array_remove_at.ab create mode 100644 src/tests/stdlib/array_remove_each.ab create mode 100644 src/tests/stdlib/array_shift.ab diff --git a/src/std/array.ab b/src/std/array.ab index 2fcf2e6f..c49cf737 100644 --- a/src/std/array.ab +++ b/src/std/array.ab @@ -26,3 +26,55 @@ pub fun includes(array, value) { let result = array_first_index(array, value) return result >= 0 } + +/// Returns the first element in the array; if the array is empty, the return +/// value is undefined. +pub fun first(array) { + return array[0] +} + +/// Returns the last element in the array; if the array is empty, the return +/// value is undefined. +pub fun last(array) { + let index = len(array) - 1 + return array[index] +} + +/// Removes an element at the index from the array; if the index is negative +/// or beyond the end, the return value is undefined, but the array will be +/// unchanged. +pub fun remove_at(ref array: [], index: Num): Null { + let offset = index + 1 + let length = len(array) + array = array[0..index] + array[offset..length] +} + +/// Removes an element at the index from the array, and returns it; if the +/// index is negative or beyond the end, the return value is undefined, but +/// the array will be unchanged. +pub fun extract_at(ref array, index) { + let element = array[index] + let offset = index + 1 + let length = len(array) + array = array[0..index] + array[offset..length] + return element +} + +/// Removes the last element from the array, and returns it; if the array +/// is empty, the return value is undefined, but the array will be unchanged. +pub fun pop(ref array) { + let length = len(array) + let index = length - 1 + let element = array[index] + array = array[0..index] + return element +} + +/// Removes the first element from the array, and returns it; if the array +/// is empty, the return value is undefined, but the array will be unchanged. +pub fun shift(ref array) { + let length = len(array) + let element = array[0] + array = array[1..length] + return element +} diff --git a/src/tests/stdlib/array_extract_at.ab b/src/tests/stdlib/array_extract_at.ab new file mode 100644 index 00000000..db8b8618 --- /dev/null +++ b/src/tests/stdlib/array_extract_at.ab @@ -0,0 +1,25 @@ +import { extract_at } from "std/array" + +// Output +// Value at -5: "" (4) [zero one two three] +// Value at -4: "zero" (4) [zero one two three] +// Value at -3: "one" (4) [zero one two three] +// Value at -2: "two" (4) [zero one two three] +// Value at -1: "three" (4) [zero one two three] +// Value at 0: "zero" (3) [one two three] +// Value at 1: "one" (3) [zero two three] +// Value at 2: "two" (3) [zero one three] +// Value at 3: "three" (3) [zero one two] +// Value at 4: "" (4) [zero one two three] + +fun test_extract(data: [Text], index: Num): Null { + let value = extract_at(data, index) + echo "Value at {index}: \"{value}\" ({len(data)}) [{data}]" +} + +main { + let numbers = ["zero", "one", "two", "three"] + for index in -5..=4 { + test_extract(numbers, index) + } +} diff --git a/src/tests/stdlib/array_first.ab b/src/tests/stdlib/array_first.ab new file mode 100644 index 00000000..66f626a6 --- /dev/null +++ b/src/tests/stdlib/array_first.ab @@ -0,0 +1,17 @@ +import { first } from "std/array" + +// Output +// First of numbers: "zero" (4) [zero one two three] +// First of empty: "" (0) [] + +fun test_first(label: Text, data: [Text]): Null { + let value = first(data) + echo "First of {label}: \"{value}\" ({len(data)}) [{data}]" +} + +main { + let numbers = ["zero", "one", "two", "three"] + let empty = [Text] + test_first("numbers", numbers) + test_first("empty", empty) +} diff --git a/src/tests/stdlib/array_first_index.ab b/src/tests/stdlib/array_first_index.ab index 376131fc..38083699 100644 --- a/src/tests/stdlib/array_first_index.ab +++ b/src/tests/stdlib/array_first_index.ab @@ -1,8 +1,22 @@ -import * from "std/array" +import { array_first_index } from "std/array" // Output -// 2 +// Index of "zero": 0 +// Index of "one": 1 +// Index of "two": 2 +// Index of "three": 3 +// Index of "four": -1 + +fun test_index(data: [Text], value: Text): Null { + let index = array_first_index(data, value) + echo "Index of \"{value}\": {index}" +} main { - echo array_first_index([1, 2, 3, 4], 3) -} + let numbers = ["zero", "one", "two", "three", "two", "one", "zero"] + test_index(numbers, "zero") + test_index(numbers, "one") + test_index(numbers, "two") + test_index(numbers, "three") + test_index(numbers, "four") +} diff --git a/src/tests/stdlib/array_last.ab b/src/tests/stdlib/array_last.ab new file mode 100644 index 00000000..4e397948 --- /dev/null +++ b/src/tests/stdlib/array_last.ab @@ -0,0 +1,17 @@ +import { last } from "std/array" + +// Output +// Last of numbers: "three" (4) [zero one two three] +// Last of empty: "" (0) [] + +fun test_last(label: Text, data: [Text]): Null { + let value = last(data) + echo "Last of {label}: \"{value}\" ({len(data)}) [{data}]" +} + +main { + let numbers = ["zero", "one", "two", "three"] + let empty = [Text] + test_last("numbers", numbers) + test_last("empty", empty) +} diff --git a/src/tests/stdlib/array_pop.ab b/src/tests/stdlib/array_pop.ab new file mode 100644 index 00000000..0b038be0 --- /dev/null +++ b/src/tests/stdlib/array_pop.ab @@ -0,0 +1,17 @@ +import { pop } from "std/array" + +// Output +// Popped from numbers: "three" (3) [zero one two] +// Popped from empty: "" (0) [] + +fun test_pop(label: Text, data: [Text]): Null { + let value = pop(data) + echo "Popped from {label}: \"{value}\" ({len(data)}) [{data}]" +} + +main { + let numbers = ["zero", "one", "two", "three"] + let empty = [Text] + test_pop("numbers", numbers) + test_pop("empty", empty) +} diff --git a/src/tests/stdlib/array_remove_at.ab b/src/tests/stdlib/array_remove_at.ab new file mode 100644 index 00000000..0f0dcd71 --- /dev/null +++ b/src/tests/stdlib/array_remove_at.ab @@ -0,0 +1,25 @@ +import { remove_at } from "std/array" + +// Output +// Array after -5: (4) [zero one two three] +// Array after -4: (4) [zero one two three] +// Array after -3: (4) [zero one two three] +// Array after -2: (4) [zero one two three] +// Array after -1: (4) [zero one two three] +// Array after 0: (3) [one two three] +// Array after 1: (3) [zero two three] +// Array after 2: (3) [zero one three] +// Array after 3: (3) [zero one two] +// Array after 4: (4) [zero one two three] + +fun test_remove(data: [Text], index: Num): Null { + remove_at(data, index) + echo "Array after {index}: ({len(data)}) [{data}]" +} + +main { + let numbers = ["zero", "one", "two", "three"] + for index in -5..=4 { + test_remove(numbers, index) + } +} diff --git a/src/tests/stdlib/array_remove_each.ab b/src/tests/stdlib/array_remove_each.ab new file mode 100644 index 00000000..65602e1f --- /dev/null +++ b/src/tests/stdlib/array_remove_each.ab @@ -0,0 +1,21 @@ +import { remove_at } from "std/array" + +// Output +// Array before 1: (4) [zero one two three] +// Array after 1: (3) [zero two three] +// Array after 2: (2) [zero three] +// Array after 3: (1) [zero] +// Array after 4: (1) [zero] + +main { + let numbers = ["zero", "one", "two", "three"] + echo "Array before 1: ({len(numbers)}) [{numbers}]" + remove_at(numbers, 1) + echo "Array after 1: ({len(numbers)}) [{numbers}]" + remove_at(numbers, 1) + echo "Array after 2: ({len(numbers)}) [{numbers}]" + remove_at(numbers, 1) + echo "Array after 3: ({len(numbers)}) [{numbers}]" + remove_at(numbers, 1) + echo "Array after 4: ({len(numbers)}) [{numbers}]" +} diff --git a/src/tests/stdlib/array_search.ab b/src/tests/stdlib/array_search.ab index e6bf532e..8e4cb74c 100644 --- a/src/tests/stdlib/array_search.ab +++ b/src/tests/stdlib/array_search.ab @@ -1,9 +1,22 @@ -import * from "std/array" +import { array_search } from "std/array" // Output -// 6 +// Indices of "zero": [0 6] +// Indices of "one": [1 5] +// Indices of "two": [2 4] +// Indices of "three": [3] +// Indices of "four": [] + +fun test_search(data: [Text], value: Text): Null { + let indices = array_search(data, value) + echo "Indices of \"{value}\": [{indices}]" +} main { - let result = array_search([1, 2, 3, 4, 3], 3) - echo result[0]+result[1] -} + let numbers = ["zero", "one", "two", "three", "two", "one", "zero"] + test_search(numbers, "zero") + test_search(numbers, "one") + test_search(numbers, "two") + test_search(numbers, "three") + test_search(numbers, "four") +} diff --git a/src/tests/stdlib/array_shift.ab b/src/tests/stdlib/array_shift.ab new file mode 100644 index 00000000..eba32b83 --- /dev/null +++ b/src/tests/stdlib/array_shift.ab @@ -0,0 +1,17 @@ +import { shift } from "std/array" + +// Output +// Shifted from numbers: "zero" (3) [one two three] +// Shifted from empty: "" (0) [] + +fun test_shift(label: Text, data: [Text]): Null { + let value = shift(data) + echo "Shifted from {label}: \"{value}\" ({len(data)}) [{data}]" +} + +main { + let numbers = ["zero", "one", "two", "three"] + let empty = [Text] + test_shift("numbers", numbers) + test_shift("empty", empty) +}