diff --git a/exercises/practice/linked-list/.meta/Sources/LinkedList/LinkedListExample.swift b/exercises/practice/linked-list/.meta/Sources/LinkedList/LinkedListExample.swift index 51ca7d0fd..5cbe8ead0 100644 --- a/exercises/practice/linked-list/.meta/Sources/LinkedList/LinkedListExample.swift +++ b/exercises/practice/linked-list/.meta/Sources/LinkedList/LinkedListExample.swift @@ -1,4 +1,4 @@ -class Node { +class Node : Equatable { var value: T? var next: Node? var prev: Node? @@ -10,9 +10,12 @@ class Node { self.value = value } + static func == (lhs: Node, rhs: Node) -> Bool { + return lhs.value == rhs.value + } } -class Deque { +class Deque { var count: Int = 0 var head: Node var tail: Node @@ -27,6 +30,27 @@ class Deque { return self.count == 0 } + func delete(_ value: T) { + var current: Node? = self.head + while current != nil { + if current?.value == value { + if current == self.head { + self.head = self.head.next ?? Node() + self.head.prev = nil + } else if current == self.tail { + self.tail = self.tail.prev ?? Node() + self.tail.next = nil + } else { + current?.prev?.next = current?.next + current?.next?.prev = current?.prev + } + self.count -= 1 + break + } + current = current?.next + } + } + func push(_ value: T) { let node = Node(value: value) if self.isEmpty() { diff --git a/exercises/practice/linked-list/.meta/template.swift b/exercises/practice/linked-list/.meta/template.swift new file mode 100644 index 000000000..06f466a19 --- /dev/null +++ b/exercises/practice/linked-list/.meta/template.swift @@ -0,0 +1,27 @@ +import Testing +import Foundation +@testable import {{exercise|camelCase}} + +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct {{exercise|camelCase}}Tests { + {% for case in cases %} + {% if forloop.first -%} + @Test("{{case.description}}") + {% else -%} + @Test("{{case.description}}", .enabled(if: RUNALL)) + {% endif -%} + func test{{case.description |camelCase }}() { + let deque = Deque() + {%- for operation in case.input.operations -%} + {%- if operation.operation == "count" -%} + #expect(deque.{{operation.operation}} == {{operation.expected}}) + {%- elif operation.expected -%} + #expect(deque.{{operation.operation}}() == {{operation.expected}}) + {%- else -%} + deque.{{operation.operation}}({{operation.value}}) + {%- endif -%} + {%- endfor -%} + } +{% endfor -%} +} \ No newline at end of file diff --git a/exercises/practice/linked-list/Package.swift b/exercises/practice/linked-list/Package.swift index 8805d36f1..afa713c8b 100644 --- a/exercises/practice/linked-list/Package.swift +++ b/exercises/practice/linked-list/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:6.0 import PackageDescription diff --git a/exercises/practice/linked-list/Tests/LinkedListTests/LinkedListTests.swift b/exercises/practice/linked-list/Tests/LinkedListTests/LinkedListTests.swift index 959d93669..06d8c4727 100644 --- a/exercises/practice/linked-list/Tests/LinkedListTests/LinkedListTests.swift +++ b/exercises/practice/linked-list/Tests/LinkedListTests/LinkedListTests.swift @@ -1,51 +1,206 @@ -import XCTest +import Foundation +import Testing + @testable import LinkedList -class LinkedListTests: XCTestCase { - - func testPushPop() { - let deque = Deque() - deque.push(10) - deque.push(20) - XCTAssertEqual(20, deque.pop() ?? 0 ) - XCTAssertEqual(10, deque.pop() ?? 0 ) - } - - func testPushShift() { - let deque = Deque() - deque.push(10) - deque.push(20) - XCTAssertEqual(10, deque.shift() ?? 0 ) - XCTAssertEqual(20, deque.shift() ?? 0 ) - } - - func testUnshiftShift() { - let deque = Deque() - deque.unshift(10) - deque.unshift(20) - XCTAssertEqual(20, deque.shift() ?? 0 ) - XCTAssertEqual(10, deque.shift() ?? 0 ) - } - - func testUnshiftPop() { - let deque = Deque() - deque.unshift(10) - deque.unshift(20) - XCTAssertEqual(10, deque.pop() ?? 0 ) - XCTAssertEqual(20, deque.pop() ?? 0 ) - } - - func testExampleMethodLength() { - let deque = Deque() - deque.push(10) - deque.push(20) - XCTAssertEqual(20, deque.pop() ?? 0 ) - deque.push(30) - XCTAssertEqual(10, deque.shift() ?? 0 ) - deque.unshift(40) - deque.push(50) - XCTAssertEqual(40, deque.shift() ?? 0 ) - XCTAssertEqual(50, deque.pop() ?? 0 ) - XCTAssertEqual(30, deque.shift() ?? 0 ) - } +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct LinkedListTests { + + @Test("pop gets element from the list") + func testPopGetsElementFromTheList() { + let deque = Deque() + deque.push(7) + #expect(deque.pop() == 7) + } + + @Test("push/pop respectively add/remove at the end of the list", .enabled(if: RUNALL)) + func testPushpopRespectivelyAddremoveAtTheEndOfTheList() { + let deque = Deque() + deque.push(11) + deque.push(13) + #expect(deque.pop() == 13) + #expect(deque.pop() == 11) + } + + @Test("shift gets an element from the list", .enabled(if: RUNALL)) + func testShiftGetsAnElementFromTheList() { + let deque = Deque() + deque.push(17) + #expect(deque.shift() == 17) + } + + @Test("shift gets first element from the list", .enabled(if: RUNALL)) + func testShiftGetsFirstElementFromTheList() { + let deque = Deque() + deque.push(23) + deque.push(5) + #expect(deque.shift() == 23) + #expect(deque.shift() == 5) + } + + @Test("unshift adds element at start of the list", .enabled(if: RUNALL)) + func testUnshiftAddsElementAtStartOfTheList() { + let deque = Deque() + deque.unshift(23) + deque.unshift(5) + #expect(deque.shift() == 5) + #expect(deque.shift() == 23) + } + + @Test("pop, push, shift, and unshift can be used in any order", .enabled(if: RUNALL)) + func testPopPushShiftAndUnshiftCanBeUsedInAnyOrder() { + let deque = Deque() + deque.push(1) + deque.push(2) + #expect(deque.pop() == 2) + deque.push(3) + #expect(deque.shift() == 1) + deque.unshift(4) + deque.push(5) + #expect(deque.shift() == 4) + #expect(deque.pop() == 5) + #expect(deque.shift() == 3) + } + + @Test("count an empty list", .enabled(if: RUNALL)) + func testCountAnEmptyList() { + let deque = Deque() + #expect(deque.count == 0) + } + + @Test("count a list with items", .enabled(if: RUNALL)) + func testCountAListWithItems() { + let deque = Deque() + deque.push(37) + deque.push(1) + #expect(deque.count == 2) + } + + @Test("count is correct after mutation", .enabled(if: RUNALL)) + func testCountIsCorrectAfterMutation() { + let deque = Deque() + deque.push(31) + #expect(deque.count == 1) + deque.unshift(43) + #expect(deque.count == 2) + deque.shift() + #expect(deque.count == 1) + deque.pop() + #expect(deque.count == 0) + } + + @Test("popping to empty doesn't break the list", .enabled(if: RUNALL)) + func testPoppingToEmptyDoesntBreakTheList() { + let deque = Deque() + deque.push(41) + deque.push(59) + deque.pop() + deque.pop() + deque.push(47) + #expect(deque.count == 1) + #expect(deque.pop() == 47) + } + + @Test("shifting to empty doesn't break the list", .enabled(if: RUNALL)) + func testShiftingToEmptyDoesntBreakTheList() { + let deque = Deque() + deque.push(41) + deque.push(59) + deque.shift() + deque.shift() + deque.push(47) + #expect(deque.count == 1) + #expect(deque.shift() == 47) + } + + @Test("deletes the only element", .enabled(if: RUNALL)) + func testDeletesTheOnlyElement() { + let deque = Deque() + deque.push(61) + deque.delete(61) + #expect(deque.count == 0) + } + + @Test("deletes the element with the specified value from the list", .enabled(if: RUNALL)) + func testDeletesTheElementWithTheSpecifiedValueFromTheList() { + let deque = Deque() + deque.push(71) + deque.push(83) + deque.push(79) + deque.delete(83) + #expect(deque.count == 2) + #expect(deque.pop() == 79) + #expect(deque.shift() == 71) + } + + @Test( + "deletes the element with the specified value from the list, re-assigns tail", + .enabled(if: RUNALL)) + func testDeletesTheElementWithTheSpecifiedValueFromTheListReAssignsTail() { + let deque = Deque() + deque.push(71) + deque.push(83) + deque.push(79) + deque.delete(83) + #expect(deque.count == 2) + #expect(deque.pop() == 79) + #expect(deque.pop() == 71) + } + + @Test( + "deletes the element with the specified value from the list, re-assigns head", + .enabled(if: RUNALL)) + func testDeletesTheElementWithTheSpecifiedValueFromTheListReAssignsHead() { + let deque = Deque() + deque.push(71) + deque.push(83) + deque.push(79) + deque.delete(83) + #expect(deque.count == 2) + #expect(deque.shift() == 71) + #expect(deque.shift() == 79) + } + + @Test("deletes the first of two elements", .enabled(if: RUNALL)) + func testDeletesTheFirstOfTwoElements() { + let deque = Deque() + deque.push(97) + deque.push(101) + deque.delete(97) + #expect(deque.count == 1) + #expect(deque.pop() == 101) + } + + @Test("deletes the second of two elements", .enabled(if: RUNALL)) + func testDeletesTheSecondOfTwoElements() { + let deque = Deque() + deque.push(97) + deque.push(101) + deque.delete(101) + #expect(deque.count == 1) + #expect(deque.pop() == 97) + } + + @Test("delete does not modify the list if the element is not found", .enabled(if: RUNALL)) + func testDeleteDoesNotModifyTheListIfTheElementIsNotFound() { + let deque = Deque() + deque.push(89) + deque.delete(103) + #expect(deque.count == 1) + } + + @Test("deletes only the first occurrence", .enabled(if: RUNALL)) + func testDeletesOnlyTheFirstOccurrence() { + let deque = Deque() + deque.push(73) + deque.push(9) + deque.push(9) + deque.push(107) + deque.delete(9) + #expect(deque.count == 3) + #expect(deque.pop() == 107) + #expect(deque.pop() == 9) + #expect(deque.pop() == 73) + } } diff --git a/exercises/practice/list-ops/.meta/Sources/ListOps/ListOpsExample.swift b/exercises/practice/list-ops/.meta/Sources/ListOps/ListOpsExample.swift index 0d4b54e31..b77f108d2 100644 --- a/exercises/practice/list-ops/.meta/Sources/ListOps/ListOpsExample.swift +++ b/exercises/practice/list-ops/.meta/Sources/ListOps/ListOpsExample.swift @@ -9,11 +9,13 @@ struct ListOps { return result } - static func concat(_ arrays: [T]...) -> [T] { + static func concat(_ arrays: [[T]]) -> [T] { var result = [T]() for array in arrays { - result = append(result, array) + for value in array { + result.append(value) + } } return result @@ -54,7 +56,7 @@ struct ListOps { if length(array) == 0 { return accumulated } else { - return foldLeft(Array(array.dropFirst()), accumulated: combine(accumulated, array[0]), combine: combine) + return foldLeft(Array(array.dropFirst()), accumulated: combine(array[0], accumulated), combine: combine) } } diff --git a/exercises/practice/list-ops/.meta/template.swift b/exercises/practice/list-ops/.meta/template.swift new file mode 100644 index 000000000..f62657b58 --- /dev/null +++ b/exercises/practice/list-ops/.meta/template.swift @@ -0,0 +1,54 @@ +import Testing +import Foundation +@testable import {{exercise|camelCase}} + +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct {{exercise|camelCase}}Tests { + {% outer: for case in cases %} + {%- for subCases in case.cases %} + {%- if forloop.outer.first and forloop.first %} + @Test("{{subCases.description}}") + {%- else %} + @Test("{{subCases.description}}", .enabled(if: RUNALL)) + {%- endif %} + func test{{subCases.description |camelCase }}{{ forloop.outer.counter }}() { + {%- if subCases.property == "append" %} + let list = {{exercise | camelCase}}.{{subCases.property}}({{subCases.input.list1 | defaultArray}}, {{subCases.input.list2 | defaultArray}}) + let expected = {{subCases.expected | defaultArray}} + #expect(list == expected) + {%- elif subCases.property == "concat" %} + let list = {{exercise | camelCase}}.{{subCases.property}}({{subCases.input.lists | defaultArray: "[Int]"}}) + let expected = {{subCases.expected | defaultArray}} + #expect(list == expected) + {%- elif subCases.property == "filter" %} + let list = {{exercise | camelCase}}.{{subCases.property}}({{subCases.input.list | defaultArray}}) { $0 % 2 == 1 } + let expected = {{subCases.expected | defaultArray}} + #expect(list == expected) + {%- elif subCases.property == "length" %} + let length = {{exercise | camelCase}}.{{subCases.property}}({{subCases.input.list}}) + let expected = {{subCases.expected}} + #expect(length == expected) + {%- elif subCases.property == "map" %} + let list = {{exercise | camelCase}}.{{subCases.property}}({{subCases.input.list | defaultArray}}) { $0 + 1 } + let expected = {{subCases.expected | defaultArray}} + #expect(list == expected) + {%- elif subCases.property == "foldl" or subCases.property == "foldr" %} + {%- if subCases.input.function == "(acc, el) -> el * acc" %} + let value = {{exercise | camelCase}}.{{subCases.property | listOps}}({{subCases.input.list}}, accumulated: {{subCases.input.initial}}.0) { $0 * $1 } + {%- elif subCases.input.function == "(acc, el) -> el + acc" %} + let value = {{exercise | camelCase}}.{{subCases.property | listOps}}({{subCases.input.list}}, accumulated: {{subCases.input.initial}}.0) { $0 + $1 } + {%- elif subCases.input.function == "(acc, el) -> el / acc" %} + let value = {{exercise | camelCase}}.{{subCases.property | listOps}}({{subCases.input.list}}, accumulated: {{subCases.input.initial}}.0) { $0 / $1 } + {%- endif %} + let expected = {{subCases.expected}}.0 + #expect(value == expected) + {%- elif subCases.property == "reverse" %} + let list = {{exercise | camelCase}}.{{subCases.property}}({{subCases.input.list | defaultArray}}) + let expected = {{subCases.expected | defaultArray}} + #expect(list == expected) + {%- endif %} + } + {% endfor -%} + {% endfor -%} +} \ No newline at end of file diff --git a/exercises/practice/list-ops/.meta/tests.toml b/exercises/practice/list-ops/.meta/tests.toml index d195e4111..08b1edc04 100644 --- a/exercises/practice/list-ops/.meta/tests.toml +++ b/exercises/practice/list-ops/.meta/tests.toml @@ -50,12 +50,15 @@ description = "return a list of elements whose values equal the list value trans [613b20b7-1873-4070-a3a6-70ae5f50d7cc] description = "folds (reduces) the given list from the left with a function -> empty list" +include = false [e56df3eb-9405-416a-b13a-aabb4c3b5194] description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list" +include = false [d2cf5644-aee1-4dfc-9b88-06896676fe27] description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list" +include = false [36549237-f765-4a4c-bfd9-5d3a8f7b07d2] description = "folds (reduces) the given list from the left with a function -> empty list" @@ -71,12 +74,15 @@ reimplements = "d2cf5644-aee1-4dfc-9b88-06896676fe27" [aeb576b9-118e-4a57-a451-db49fac20fdc] description = "folds (reduces) the given list from the right with a function -> empty list" +include = false [c4b64e58-313e-4c47-9c68-7764964efb8e] description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list" +include = false [be396a53-c074-4db3-8dd6-f7ed003cce7c] description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list" +include = false [17214edb-20ba-42fc-bda8-000a5ab525b0] description = "folds (reduces) the given list from the right with a function -> empty list" diff --git a/exercises/practice/list-ops/Package.swift b/exercises/practice/list-ops/Package.swift index 8007eea2e..196b6da6c 100644 --- a/exercises/practice/list-ops/Package.swift +++ b/exercises/practice/list-ops/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:6.0 import PackageDescription diff --git a/exercises/practice/list-ops/Tests/ListOpsTests/ListOpsTests.swift b/exercises/practice/list-ops/Tests/ListOpsTests/ListOpsTests.swift index 1742e5ebf..5088dbce5 100644 --- a/exercises/practice/list-ops/Tests/ListOpsTests/ListOpsTests.swift +++ b/exercises/practice/list-ops/Tests/ListOpsTests/ListOpsTests.swift @@ -1,85 +1,163 @@ -import XCTest -@testable import ListOps - -class ListOpsTests: XCTestCase { - - func testAppendEmptyLists() { - XCTAssertEqual(ListOps.append([Int](), []), []) - } - - func testAppendEmptyListToList() { - XCTAssertEqual(ListOps.append([], [1, 2, 3, 4]), [1, 2, 3, 4]) - } - - func testAppendNonemptyLists() { - XCTAssertEqual(ListOps.append([1, 2], [2, 3, 4, 5]), [1, 2, 2, 3, 4, 5]) - } - - func testConcatEmptyList() { - XCTAssertEqual(ListOps.concat([Int]()), []) - } - - func testConcatListOfLists() { - XCTAssertEqual(ListOps.concat([1, 2], [3], [], [4, 5, 6]), [1, 2, 3, 4, 5, 6]) - } - - func testFilterEmptyList() { - XCTAssertEqual(ListOps.filter([]) { $0 % 2 == 1 }, []) - } - - func testFilterNonemptyList() { - XCTAssertEqual(ListOps.filter([1, 2, 3, 4, 5]) { $0 % 2 == 1 }, [1, 3, 5]) - } - - func testLengthEmptyList() { - XCTAssertEqual(ListOps.length([]), 0) - } - - func testLengthNonemptyList() { - XCTAssertEqual(ListOps.length([1, 2, 3, 4]), 4) - } +import Foundation +import Testing - func testMapEmptyList() { - XCTAssertEqual(ListOps.map([]) { $0 + 1 }, []) - } - - func testMapNonemptyList() { - XCTAssertEqual(ListOps.map([1, 3, 5, 7]) { $0 + 1 }, [2, 4, 6, 8]) - } - - func testFoldLeftEmptyList() { - XCTAssertEqual(ListOps.foldLeft([], accumulated: 2, combine: +), 2) - } - - func testFoldLeftNonemptyListAddition() { - XCTAssertEqual(ListOps.foldLeft([1, 2, 3, 4], accumulated: 5, combine: +), 15) - } - - func testFoldLeftNonemptyListDivision() { - XCTAssertEqual(ListOps.foldLeft([2, 5], accumulated: 5, combine: /), 0) - } - - func testFoldRightEmptyList() { - XCTAssertEqual(ListOps.foldRight([], accumulated: 2, combine: *), 2) - } - - func testFoldRightNonemptyListAddition() { - XCTAssertEqual(ListOps.foldRight([1, 2, 3, 4], accumulated: 5, combine: +), 15) - } - - func testFoldRightNonemptyListDivision() { - XCTAssertEqual(ListOps.foldRight([2, 5], accumulated: 5, combine: /), 2) - } - - func testFoldRightAddString() { - XCTAssertEqual(ListOps.foldRight(["e", "x", "e", "r", "c", "i", "s", "m"], accumulated: "!", combine: +), "exercism!") - } - - func testReverseEmptyList() { - XCTAssertEqual(ListOps.reverse([Int]()), []) - } +@testable import ListOps - func testReverseNonemptyList() { - XCTAssertEqual(ListOps.reverse([1, 3, 5, 7]), [7, 5, 3, 1]) - } +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct ListOpsTests { + + @Test("empty lists") + func testEmptyLists1() { + let list = ListOps.append([Int](), [Int]()) + let expected = [Int]() + #expect(list == expected) + } + + @Test("list to empty list", .enabled(if: RUNALL)) + func testListToEmptyList1() { + let list = ListOps.append([Int](), [1, 2, 3, 4]) + let expected = [1, 2, 3, 4] + #expect(list == expected) + } + + @Test("empty list to list", .enabled(if: RUNALL)) + func testEmptyListToList1() { + let list = ListOps.append([1, 2, 3, 4], [Int]()) + let expected = [1, 2, 3, 4] + #expect(list == expected) + } + + @Test("non-empty lists", .enabled(if: RUNALL)) + func testNonEmptyLists1() { + let list = ListOps.append([1, 2], [2, 3, 4, 5]) + let expected = [1, 2, 2, 3, 4, 5] + #expect(list == expected) + } + + @Test("empty list", .enabled(if: RUNALL)) + func testEmptyList2() { + let list = ListOps.concat([[Int]]()) + let expected = [Int]() + #expect(list == expected) + } + + @Test("list of lists", .enabled(if: RUNALL)) + func testListOfLists2() { + let list = ListOps.concat([[1, 2], [3], [], [4, 5, 6]]) + let expected = [1, 2, 3, 4, 5, 6] + #expect(list == expected) + } + + @Test("list of nested lists", .enabled(if: RUNALL)) + func testListOfNestedLists2() { + let list = ListOps.concat([[[1], [2]], [[3]], [[]], [[4, 5, 6]]]) + let expected = [[1], [2], [3], [], [4, 5, 6]] + #expect(list == expected) + } + + @Test("empty list", .enabled(if: RUNALL)) + func testEmptyList3() { + let list = ListOps.filter([Int]()) { $0 % 2 == 1 } + let expected = [Int]() + #expect(list == expected) + } + + @Test("non-empty list", .enabled(if: RUNALL)) + func testNonEmptyList3() { + let list = ListOps.filter([1, 2, 3, 5]) { $0 % 2 == 1 } + let expected = [1, 3, 5] + #expect(list == expected) + } + + @Test("empty list", .enabled(if: RUNALL)) + func testEmptyList4() { + let length = ListOps.length([]) + let expected = 0 + #expect(length == expected) + } + + @Test("non-empty list", .enabled(if: RUNALL)) + func testNonEmptyList4() { + let length = ListOps.length([1, 2, 3, 4]) + let expected = 4 + #expect(length == expected) + } + + @Test("empty list", .enabled(if: RUNALL)) + func testEmptyList5() { + let list = ListOps.map([Int]()) { $0 + 1 } + let expected = [Int]() + #expect(list == expected) + } + + @Test("non-empty list", .enabled(if: RUNALL)) + func testNonEmptyList5() { + let list = ListOps.map([1, 3, 5, 7]) { $0 + 1 } + let expected = [2, 4, 6, 8] + #expect(list == expected) + } + + @Test("empty list", .enabled(if: RUNALL)) + func testEmptyList6() { + let value = ListOps.foldLeft([], accumulated: 2.0) { $0 * $1 } + let expected = 2.0 + #expect(value == expected) + } + + @Test("direction independent function applied to non-empty list", .enabled(if: RUNALL)) + func testDirectionIndependentFunctionAppliedToNonEmptyList6() { + let value = ListOps.foldLeft([1, 2, 3, 4], accumulated: 5.0) { $0 + $1 } + let expected = 15.0 + #expect(value == expected) + } + + @Test("direction dependent function applied to non-empty list", .enabled(if: RUNALL)) + func testDirectionDependentFunctionAppliedToNonEmptyList6() { + let value = ListOps.foldLeft([1, 2, 3, 4], accumulated: 24.0) { $0 / $1 } + let expected = 64.0 + #expect(value == expected) + } + + @Test("empty list", .enabled(if: RUNALL)) + func testEmptyList7() { + let value = ListOps.foldRight([], accumulated: 2.0) { $0 * $1 } + let expected = 2.0 + #expect(value == expected) + } + + @Test("direction independent function applied to non-empty list", .enabled(if: RUNALL)) + func testDirectionIndependentFunctionAppliedToNonEmptyList7() { + let value = ListOps.foldRight([1, 2, 3, 4], accumulated: 5.0) { $0 + $1 } + let expected = 15.0 + #expect(value == expected) + } + + @Test("direction dependent function applied to non-empty list", .enabled(if: RUNALL)) + func testDirectionDependentFunctionAppliedToNonEmptyList7() { + let value = ListOps.foldRight([1, 2, 3, 4], accumulated: 24.0) { $0 / $1 } + let expected = 9.0 + #expect(value == expected) + } + + @Test("empty list", .enabled(if: RUNALL)) + func testEmptyList8() { + let list = ListOps.reverse([Int]()) + let expected = [Int]() + #expect(list == expected) + } + + @Test("non-empty list", .enabled(if: RUNALL)) + func testNonEmptyList8() { + let list = ListOps.reverse([1, 3, 5, 7]) + let expected = [7, 5, 3, 1] + #expect(list == expected) + } + + @Test("list of lists is not flattened", .enabled(if: RUNALL)) + func testListOfListsIsNotFlattened8() { + let list = ListOps.reverse([[1, 2], [3], [], [4, 5, 6]]) + let expected = [[4, 5, 6], [], [3], [1, 2]] + #expect(list == expected) + } } diff --git a/generator/Sources/Generator/generator-plugins.swift b/generator/Sources/Generator/generator-plugins.swift index 353bd95bd..43d1f767e 100644 --- a/generator/Sources/Generator/generator-plugins.swift +++ b/generator/Sources/Generator/generator-plugins.swift @@ -244,6 +244,23 @@ class GeneratorPlugins { return "// Something else ..." } + ext.registerFilter("listOps") { (value: Any?) in + if let inputString = value as? String { + return inputString.replacingOccurrences(of: "foldl", with: "foldLeft") + .replacingOccurrences(of: "foldr", with: "foldRight") + } + return nil + } + + ext.registerFilter("defaultArray") { (value: Any?, args ) in + if let inputArray = value as? [Any?] { + let type = args.first as? String ?? "Int" + return inputArray.isEmpty ? "[\(type)]()" : inputArray + } + let type = args.first as? String ?? "Int" + return "[\(type)]()" + } + let environment = Environment(extensions: [ext]) return environment }