From 2f62c9aeb775bd9606cdda6ba13eb4ccdabf49fa Mon Sep 17 00:00:00 2001 From: homersimpsons Date: Tue, 30 Jan 2024 23:07:44 +0000 Subject: [PATCH 1/4] Add ListOps exercise --- config.json | 8 + .../list-ops/.docs/instructions.append.md | 16 ++ .../practice/list-ops/.docs/instructions.md | 19 ++ exercises/practice/list-ops/.meta/config.json | 15 ++ exercises/practice/list-ops/.meta/example.php | 99 ++++++++ exercises/practice/list-ops/.meta/tests.toml | 106 +++++++++ exercises/practice/list-ops/ListOps.php | 80 +++++++ exercises/practice/list-ops/ListOpsTest.php | 225 ++++++++++++++++++ 8 files changed, 568 insertions(+) create mode 100644 exercises/practice/list-ops/.docs/instructions.append.md create mode 100644 exercises/practice/list-ops/.docs/instructions.md create mode 100644 exercises/practice/list-ops/.meta/config.json create mode 100644 exercises/practice/list-ops/.meta/example.php create mode 100644 exercises/practice/list-ops/.meta/tests.toml create mode 100644 exercises/practice/list-ops/ListOps.php create mode 100644 exercises/practice/list-ops/ListOpsTest.php diff --git a/config.json b/config.json index c08f392e..cb324d8a 100644 --- a/config.json +++ b/config.json @@ -932,6 +932,14 @@ "practices": [], "prerequisites": [], "difficulty": 2 + }, + { + "slug": "list-ops", + "name": "List Ops", + "uuid": "56f36086-96a8-4941-8c46-b563f02e5b09", + "practices": [], + "prerequisites": [], + "difficulty": 7 } ] }, diff --git a/exercises/practice/list-ops/.docs/instructions.append.md b/exercises/practice/list-ops/.docs/instructions.append.md new file mode 100644 index 00000000..fd24f17e --- /dev/null +++ b/exercises/practice/list-ops/.docs/instructions.append.md @@ -0,0 +1,16 @@ +## Callable + +In PHP there is a concept of [callable](https://www.php.net/manual/en/language.types.callable.php). + +Those can take multiple forms, but we will focus on [anonymous functions](https://www.php.net/manual/en/functions.anonymous.php). + +It is possible to create an anonymous function in a variable and call it with parameters: + +```php +$double = function ($number) { + return $number * 2; +} + +$double(2); // returns 4 +$double(4); // returns 8 +``` \ No newline at end of file diff --git a/exercises/practice/list-ops/.docs/instructions.md b/exercises/practice/list-ops/.docs/instructions.md new file mode 100644 index 00000000..ebc5dffe --- /dev/null +++ b/exercises/practice/list-ops/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +Implement basic list operations. + +In functional languages list operations like `length`, `map`, and `reduce` are very common. +Implement a series of basic list operations, without using existing functions. + +The precise number and names of the operations to be implemented will be track dependent to avoid conflicts with existing names, but the general operations you will implement include: + +- `append` (_given two lists, add all items in the second list to the end of the first list_); +- `concatenate` (_given a series of lists, combine all items in all lists into one flattened list_); +- `filter` (_given a predicate and a list, return the list of all items for which `predicate(item)` is True_); +- `length` (_given a list, return the total number of items within it_); +- `map` (_given a function and a list, return the list of the results of applying `function(item)` on all items_); +- `foldl` (_given a function, a list, and initial accumulator, fold (reduce) each item into the accumulator from the left_); +- `foldr` (_given a function, a list, and an initial accumulator, fold (reduce) each item into the accumulator from the right_); +- `reverse` (_given a list, return a list with all the original items, but in reversed order_). + +Note, the ordering in which arguments are passed to the fold functions (`foldl`, `foldr`) is significant. diff --git a/exercises/practice/list-ops/.meta/config.json b/exercises/practice/list-ops/.meta/config.json new file mode 100644 index 00000000..0cde5e27 --- /dev/null +++ b/exercises/practice/list-ops/.meta/config.json @@ -0,0 +1,15 @@ +{ + "authors": ["homersimpsons"], + "files": { + "solution": [ + "ListOps.php" + ], + "test": [ + "ListOpsTest.php" + ], + "example": [ + ".meta/example.php" + ] + }, + "blurb": "Implement basic list operations." +} diff --git a/exercises/practice/list-ops/.meta/example.php b/exercises/practice/list-ops/.meta/example.php new file mode 100644 index 00000000..4497251f --- /dev/null +++ b/exercises/practice/list-ops/.meta/example.php @@ -0,0 +1,99 @@ +. + * + * To disable strict typing, comment out the directive below. + */ + +declare(strict_types=1); + +class ListOps +{ + public static function append(array $list1, array $list2): array + { + foreach ($list2 as $el) { + $list1[] = $el; + } + return $list1; + } + + public static function concat(array $list1, array ...$listn): array + { + foreach ($listn as $list) { + $list1 = self::append($list1, $list); + } + return $list1; + } + + public static function filter(callable $predicate, array $list): array + { + $result = []; + foreach ($list as $el) { + if ($predicate($el)) { + $result[] = $el; + } + } + return $result; + } + + public static function length(array $list): int + { + $count = 0; + foreach ($list as $_el) { + $count++; + } + return $count; + } + + public static function map(callable $function, array $list): array + { + $result = []; + foreach ($list as $el) { + $result[] = $function($el); + } + return $result; + } + + public static function foldl(callable $function, array $list, $accumulator) + { + foreach ($list as $el) { + $accumulator = $function($accumulator, $el); + } + return $accumulator; + } + + public static function foldr(callable $function, array $list, $accumulator) + { + while (self::length($list) > 0) { + $el = array_pop($list); + $accumulator = $function($accumulator, $el); + } + return $accumulator; + } + + public static function reverse(array $list): array + { + $result = []; + while (self::length($list) > 0) { + $result[] = array_pop($list); + } + return $result; + } +} diff --git a/exercises/practice/list-ops/.meta/tests.toml b/exercises/practice/list-ops/.meta/tests.toml new file mode 100644 index 00000000..08b1edc0 --- /dev/null +++ b/exercises/practice/list-ops/.meta/tests.toml @@ -0,0 +1,106 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[485b9452-bf94-40f7-a3db-c3cf4850066a] +description = "append entries to a list and return the new list -> empty lists" + +[2c894696-b609-4569-b149-8672134d340a] +description = "append entries to a list and return the new list -> list to empty list" + +[e842efed-3bf6-4295-b371-4d67a4fdf19c] +description = "append entries to a list and return the new list -> empty list to list" + +[71dcf5eb-73ae-4a0e-b744-a52ee387922f] +description = "append entries to a list and return the new list -> non-empty lists" + +[28444355-201b-4af2-a2f6-5550227bde21] +description = "concatenate a list of lists -> empty list" + +[331451c1-9573-42a1-9869-2d06e3b389a9] +description = "concatenate a list of lists -> list of lists" + +[d6ecd72c-197f-40c3-89a4-aa1f45827e09] +description = "concatenate a list of lists -> list of nested lists" + +[0524fba8-3e0f-4531-ad2b-f7a43da86a16] +description = "filter list returning only values that satisfy the filter function -> empty list" + +[88494bd5-f520-4edb-8631-88e415b62d24] +description = "filter list returning only values that satisfy the filter function -> non-empty list" + +[1cf0b92d-8d96-41d5-9c21-7b3c37cb6aad] +description = "returns the length of a list -> empty list" + +[d7b8d2d9-2d16-44c4-9a19-6e5f237cb71e] +description = "returns the length of a list -> non-empty list" + +[c0bc8962-30e2-4bec-9ae4-668b8ecd75aa] +description = "return a list of elements whose values equal the list value transformed by the mapping function -> empty list" + +[11e71a95-e78b-4909-b8e4-60cdcaec0e91] +description = "return a list of elements whose values equal the list value transformed by the mapping function -> non-empty list" + +[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" +reimplements = "613b20b7-1873-4070-a3a6-70ae5f50d7cc" + +[7a626a3c-03ec-42bc-9840-53f280e13067] +description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list" +reimplements = "e56df3eb-9405-416a-b13a-aabb4c3b5194" + +[d7fcad99-e88e-40e1-a539-4c519681f390] +description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list" +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" +reimplements = "aeb576b9-118e-4a57-a451-db49fac20fdc" + +[e1c64db7-9253-4a3d-a7c4-5273b9e2a1bd] +description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list" +reimplements = "c4b64e58-313e-4c47-9c68-7764964efb8e" + +[8066003b-f2ff-437e-9103-66e6df474844] +description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list" +reimplements = "be396a53-c074-4db3-8dd6-f7ed003cce7c" + +[94231515-050e-4841-943d-d4488ab4ee30] +description = "reverse the elements of the list -> empty list" + +[fcc03d1e-42e0-4712-b689-d54ad761f360] +description = "reverse the elements of the list -> non-empty list" + +[40872990-b5b8-4cb8-9085-d91fc0d05d26] +description = "reverse the elements of the list -> list of lists is not flattened" diff --git a/exercises/practice/list-ops/ListOps.php b/exercises/practice/list-ops/ListOps.php new file mode 100644 index 00000000..0fa51abf --- /dev/null +++ b/exercises/practice/list-ops/ListOps.php @@ -0,0 +1,80 @@ +. + * + * To disable strict typing, comment out the directive below. + */ + +declare(strict_types=1); + +class ListOps +{ + public static function append(array $list1, array $list2): array + { + throw new \BadMethodCallException("Implement the append function"); + } + + public static function concat(array $list1, array ...$listn): array + { + throw new \BadMethodCallException("Implement the concat function"); + } + + /** + * @param callable(mixed $el): bool $predicate + */ + public static function filter(callable $predicate, array $list): array + { + throw new \BadMethodCallException("Implement the filter function"); + } + + public static function length(array $list): int + { + throw new \BadMethodCallException("Implement the length function"); + } + + /** + * @param callable(mixed $el): mixed $function + */ + public static function map(callable $function, array $list): array + { + throw new \BadMethodCallException("Implement the map function"); + } + + /** + * @param callable(mixed $acc, mixed $el): mixed $function + */ + public static function foldl(callable $function, array $list, $accumulator) + { + throw new \BadMethodCallException("Implement the foldl function"); + } + + /** + * @param callable(mixed $acc, mixed $el): mixed $function + */ + public static function foldr(callable $function, array $list, $accumulator) + { + throw new \BadMethodCallException("Implement the foldr function"); + } + + public static function reverse(array $list): array + { + throw new \BadMethodCallException("Implement the reverse function"); + } +} diff --git a/exercises/practice/list-ops/ListOpsTest.php b/exercises/practice/list-ops/ListOpsTest.php new file mode 100644 index 00000000..74cddae9 --- /dev/null +++ b/exercises/practice/list-ops/ListOpsTest.php @@ -0,0 +1,225 @@ +. + * + * To disable strict typing, comment out the directive below. + */ + +declare(strict_types=1); + +use PHPUnit\Framework\ExpectationFailedException; + +class ListOpsTest extends PHPUnit\Framework\TestCase +{ + public static function setUpBeforeClass(): void + { + require_once 'ListOps.php'; + } + + /** + * @testdox append entries to a list and return the new list -> empty lists + */ + public function testAppendEmptyLists() + { + $this->assertEquals([], ListOps::append([], [])); + } + + /** + * @testdox append entries to a list and return the new list -> empty list to list + */ + public function testAppendNonEmptyListToEmptyList() + { + $this->assertEquals([1, 2, 3, 4], ListOps::append([1, 2, 3, 4], [])); + } + + /** + * @testdox append entries to a list and return the new list -> non-empty lists + */ + public function testAppendNonEmptyLists() + { + $this->assertEquals([1, 2, 2, 3, 4, 5], ListOps::append([1, 2], [2, 3, 4, 5])); + } + + /** + * @testdox concat lists and lists of lists into new list -> empty list + */ + public function testConcatEmptyLists() + { + $this->assertEquals([], ListOps::concat([], [])); + } + + /** + * @testdox concat lists and lists of lists into new list -> list of lists + */ + public function testConcatLists() + { + $this->assertEquals([1, 2, 3, 4, 5, 6], ListOps::concat([1, 2], [3], [], [4, 5, 6])); + } + + /** + * @testdox filter list returning only values that satisfy the filter function -> empty list + */ + public function testFilterEmptyList() + { + $this->assertEquals( + [], + ListOps::filter(static fn ($el) => $el % 2 === 1, []) + ); + } + + /** + * @testdox filter list returning only values that satisfy the filter function -> non empty list + */ + public function testFilterNonEmptyList() + { + $this->assertEquals( + [1, 3, 5], + ListOps::filter(static fn ($el) => $el % 2 === 1, [1, 2, 3, 5]) + ); + } + + /** + * @testdox returns the length of a list -> empty list + */ + public function testLengthEmptyList() + { + $this->assertEquals(0, ListOps::length([])); + } + + /** + * @testdox returns the length of a list -> non-empty list + */ + public function testLengthNonEmptyList() + { + $this->assertEquals(4, ListOps::length([1, 2, 3, 4])); + } + + /** + * @testdox returns a list of elements whose values equal the list value transformed by the mapping function -> empty list + */ + public function testMapEmptyList() + { + $this->assertEquals( + [], + ListOps::map(static fn ($el) => $el + 1, []) + ); + } + + /** + * @testdox returns a list of elements whose values equal the list value transformed by the mapping function -> non-empty list + */ + public function testMapNonEmptyList() + { + $this->assertEquals( + [2, 4, 6, 8], + ListOps::map(static fn ($el) => $el + 1, [1, 3, 5, 7]) + ); + } + + /** + * @testdox folds (reduces) the given list from the left with a function -> empty list + */ + public function testFoldlEmptyList() + { + $this->assertEquals( + 2, + ListOps::foldl(static fn ($acc, $el) => $el * $acc, [], 2) + ); + } + + /** + * @testdox folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list + */ + public function testFoldlDirectionIndependentNonEmptyList() + { + $this->assertEquals( + 15, + ListOps::foldl(static fn ($acc, $el) => $acc + $el, [1, 2, 3, 4], 5) + ); + } + + /** + * @testdox folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list + */ + public function testFoldlDirectionDependentNonEmptyList() + { + $this->assertEquals( + 64, + ListOps::foldl(static fn ($acc, $el) => $el / $acc, [1, 2, 3, 4], 24) + ); + } + + /** + * @testdox folds (reduces) the given list from the right with a function -> empty list + */ + public function testFoldrEmptyList() + { + $this->assertEquals( + 2, + ListOps::foldr(static fn ($acc, $el) => $el * $acc, [], 2) + ); + } + + /** + * @testdox folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list + */ + public function testFoldrDirectionIndependentNonEmptyList() + { + $this->assertEquals( + 15, + ListOps::foldr(static fn ($acc, $el) => $acc + $el, [1, 2, 3, 4], 5) + ); + } + + /** + * @testdox folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list + */ + public function testFoldrDirectionDependentNonEmptyList() + { + $this->assertEquals( + 9, + ListOps::foldr(static fn ($acc, $el) => $el / $acc, [1, 2, 3, 4], 24) + ); + } + + /** + * @testdox reverse the elements of a list -> empty list + */ + public function testReverseEmptyList() + { + $this->assertEquals([], ListOps::reverse([])); + } + + /** + * @testdox reverse the elements of a list -> non-empty list + */ + public function testReverseNonEmptyList() + { + $this->assertEquals([7, 5, 3, 1], ListOps::reverse([1, 3, 5, 7])); + } + + /** + * @testdox reverse the elements of a list -> list of lists is not flattened + */ + public function testReverseNonEmptyListIsNotFlattened() + { + $this->assertEquals([[4, 5, 6], [], [3], [1, 2]], ListOps::reverse([[1, 2], [3], [], [4, 5, 6]])); + } +} From adba505f5d5c87198bb1220dda846e295c942f2a Mon Sep 17 00:00:00 2001 From: homersimpsons Date: Thu, 15 Feb 2024 18:27:25 +0000 Subject: [PATCH 2/4] ListOps: do not use static This will ease the exercise a bit. --- config.json | 2 +- exercises/practice/list-ops/.meta/example.php | 16 ++--- exercises/practice/list-ops/ListOps.php | 16 ++--- exercises/practice/list-ops/ListOpsTest.php | 60 ++++++++++++------- phpcs-php.xml | 2 + 5 files changed, 59 insertions(+), 37 deletions(-) diff --git a/config.json b/config.json index cb324d8a..383107cf 100644 --- a/config.json +++ b/config.json @@ -939,7 +939,7 @@ "uuid": "56f36086-96a8-4941-8c46-b563f02e5b09", "practices": [], "prerequisites": [], - "difficulty": 7 + "difficulty": 6 } ] }, diff --git a/exercises/practice/list-ops/.meta/example.php b/exercises/practice/list-ops/.meta/example.php index 4497251f..c31d918c 100644 --- a/exercises/practice/list-ops/.meta/example.php +++ b/exercises/practice/list-ops/.meta/example.php @@ -26,7 +26,7 @@ class ListOps { - public static function append(array $list1, array $list2): array + public function append(array $list1, array $list2): array { foreach ($list2 as $el) { $list1[] = $el; @@ -34,7 +34,7 @@ public static function append(array $list1, array $list2): array return $list1; } - public static function concat(array $list1, array ...$listn): array + public function concat(array $list1, array ...$listn): array { foreach ($listn as $list) { $list1 = self::append($list1, $list); @@ -42,7 +42,7 @@ public static function concat(array $list1, array ...$listn): array return $list1; } - public static function filter(callable $predicate, array $list): array + public function filter(callable $predicate, array $list): array { $result = []; foreach ($list as $el) { @@ -53,7 +53,7 @@ public static function filter(callable $predicate, array $list): array return $result; } - public static function length(array $list): int + public function length(array $list): int { $count = 0; foreach ($list as $_el) { @@ -62,7 +62,7 @@ public static function length(array $list): int return $count; } - public static function map(callable $function, array $list): array + public function map(callable $function, array $list): array { $result = []; foreach ($list as $el) { @@ -71,7 +71,7 @@ public static function map(callable $function, array $list): array return $result; } - public static function foldl(callable $function, array $list, $accumulator) + public function foldl(callable $function, array $list, $accumulator) { foreach ($list as $el) { $accumulator = $function($accumulator, $el); @@ -79,7 +79,7 @@ public static function foldl(callable $function, array $list, $accumulator) return $accumulator; } - public static function foldr(callable $function, array $list, $accumulator) + public function foldr(callable $function, array $list, $accumulator) { while (self::length($list) > 0) { $el = array_pop($list); @@ -88,7 +88,7 @@ public static function foldr(callable $function, array $list, $accumulator) return $accumulator; } - public static function reverse(array $list): array + public function reverse(array $list): array { $result = []; while (self::length($list) > 0) { diff --git a/exercises/practice/list-ops/ListOps.php b/exercises/practice/list-ops/ListOps.php index 0fa51abf..57510452 100644 --- a/exercises/practice/list-ops/ListOps.php +++ b/exercises/practice/list-ops/ListOps.php @@ -26,12 +26,12 @@ class ListOps { - public static function append(array $list1, array $list2): array + public function append(array $list1, array $list2): array { throw new \BadMethodCallException("Implement the append function"); } - public static function concat(array $list1, array ...$listn): array + public function concat(array $list1, array ...$listn): array { throw new \BadMethodCallException("Implement the concat function"); } @@ -39,12 +39,12 @@ public static function concat(array $list1, array ...$listn): array /** * @param callable(mixed $el): bool $predicate */ - public static function filter(callable $predicate, array $list): array + public function filter(callable $predicate, array $list): array { throw new \BadMethodCallException("Implement the filter function"); } - public static function length(array $list): int + public function length(array $list): int { throw new \BadMethodCallException("Implement the length function"); } @@ -52,7 +52,7 @@ public static function length(array $list): int /** * @param callable(mixed $el): mixed $function */ - public static function map(callable $function, array $list): array + public function map(callable $function, array $list): array { throw new \BadMethodCallException("Implement the map function"); } @@ -60,7 +60,7 @@ public static function map(callable $function, array $list): array /** * @param callable(mixed $acc, mixed $el): mixed $function */ - public static function foldl(callable $function, array $list, $accumulator) + public function foldl(callable $function, array $list, $accumulator) { throw new \BadMethodCallException("Implement the foldl function"); } @@ -68,12 +68,12 @@ public static function foldl(callable $function, array $list, $accumulator) /** * @param callable(mixed $acc, mixed $el): mixed $function */ - public static function foldr(callable $function, array $list, $accumulator) + public function foldr(callable $function, array $list, $accumulator) { throw new \BadMethodCallException("Implement the foldr function"); } - public static function reverse(array $list): array + public function reverse(array $list): array { throw new \BadMethodCallException("Implement the reverse function"); } diff --git a/exercises/practice/list-ops/ListOpsTest.php b/exercises/practice/list-ops/ListOpsTest.php index 74cddae9..337cf3d0 100644 --- a/exercises/practice/list-ops/ListOpsTest.php +++ b/exercises/practice/list-ops/ListOpsTest.php @@ -38,7 +38,8 @@ public static function setUpBeforeClass(): void */ public function testAppendEmptyLists() { - $this->assertEquals([], ListOps::append([], [])); + $listOps = new ListOps(); + $this->assertEquals([], $listOps->append([], [])); } /** @@ -46,7 +47,8 @@ public function testAppendEmptyLists() */ public function testAppendNonEmptyListToEmptyList() { - $this->assertEquals([1, 2, 3, 4], ListOps::append([1, 2, 3, 4], [])); + $listOps = new ListOps(); + $this->assertEquals([1, 2, 3, 4], $listOps->append([1, 2, 3, 4], [])); } /** @@ -54,7 +56,8 @@ public function testAppendNonEmptyListToEmptyList() */ public function testAppendNonEmptyLists() { - $this->assertEquals([1, 2, 2, 3, 4, 5], ListOps::append([1, 2], [2, 3, 4, 5])); + $listOps = new ListOps(); + $this->assertEquals([1, 2, 2, 3, 4, 5], $listOps->append([1, 2], [2, 3, 4, 5])); } /** @@ -62,7 +65,8 @@ public function testAppendNonEmptyLists() */ public function testConcatEmptyLists() { - $this->assertEquals([], ListOps::concat([], [])); + $listOps = new ListOps(); + $this->assertEquals([], $listOps->concat([], [])); } /** @@ -70,7 +74,8 @@ public function testConcatEmptyLists() */ public function testConcatLists() { - $this->assertEquals([1, 2, 3, 4, 5, 6], ListOps::concat([1, 2], [3], [], [4, 5, 6])); + $listOps = new ListOps(); + $this->assertEquals([1, 2, 3, 4, 5, 6], $listOps->concat([1, 2], [3], [], [4, 5, 6])); } /** @@ -78,9 +83,10 @@ public function testConcatLists() */ public function testFilterEmptyList() { + $listOps = new ListOps(); $this->assertEquals( [], - ListOps::filter(static fn ($el) => $el % 2 === 1, []) + $listOps->filter(static fn ($el) => $el % 2 === 1, []) ); } @@ -89,9 +95,10 @@ public function testFilterEmptyList() */ public function testFilterNonEmptyList() { + $listOps = new ListOps(); $this->assertEquals( [1, 3, 5], - ListOps::filter(static fn ($el) => $el % 2 === 1, [1, 2, 3, 5]) + $listOps->filter(static fn ($el) => $el % 2 === 1, [1, 2, 3, 5]) ); } @@ -100,7 +107,8 @@ public function testFilterNonEmptyList() */ public function testLengthEmptyList() { - $this->assertEquals(0, ListOps::length([])); + $listOps = new ListOps(); + $this->assertEquals(0, $listOps->length([])); } /** @@ -108,7 +116,8 @@ public function testLengthEmptyList() */ public function testLengthNonEmptyList() { - $this->assertEquals(4, ListOps::length([1, 2, 3, 4])); + $listOps = new ListOps(); + $this->assertEquals(4, $listOps->length([1, 2, 3, 4])); } /** @@ -116,9 +125,10 @@ public function testLengthNonEmptyList() */ public function testMapEmptyList() { + $listOps = new ListOps(); $this->assertEquals( [], - ListOps::map(static fn ($el) => $el + 1, []) + $listOps->map(static fn ($el) => $el + 1, []) ); } @@ -127,9 +137,10 @@ public function testMapEmptyList() */ public function testMapNonEmptyList() { + $listOps = new ListOps(); $this->assertEquals( [2, 4, 6, 8], - ListOps::map(static fn ($el) => $el + 1, [1, 3, 5, 7]) + $listOps->map(static fn ($el) => $el + 1, [1, 3, 5, 7]) ); } @@ -138,9 +149,10 @@ public function testMapNonEmptyList() */ public function testFoldlEmptyList() { + $listOps = new ListOps(); $this->assertEquals( 2, - ListOps::foldl(static fn ($acc, $el) => $el * $acc, [], 2) + $listOps->foldl(static fn ($acc, $el) => $el * $acc, [], 2) ); } @@ -149,9 +161,10 @@ public function testFoldlEmptyList() */ public function testFoldlDirectionIndependentNonEmptyList() { + $listOps = new ListOps(); $this->assertEquals( 15, - ListOps::foldl(static fn ($acc, $el) => $acc + $el, [1, 2, 3, 4], 5) + $listOps->foldl(static fn ($acc, $el) => $acc + $el, [1, 2, 3, 4], 5) ); } @@ -160,9 +173,10 @@ public function testFoldlDirectionIndependentNonEmptyList() */ public function testFoldlDirectionDependentNonEmptyList() { + $listOps = new ListOps(); $this->assertEquals( 64, - ListOps::foldl(static fn ($acc, $el) => $el / $acc, [1, 2, 3, 4], 24) + $listOps->foldl(static fn ($acc, $el) => $el / $acc, [1, 2, 3, 4], 24) ); } @@ -171,9 +185,10 @@ public function testFoldlDirectionDependentNonEmptyList() */ public function testFoldrEmptyList() { + $listOps = new ListOps(); $this->assertEquals( 2, - ListOps::foldr(static fn ($acc, $el) => $el * $acc, [], 2) + $listOps->foldr(static fn ($acc, $el) => $el * $acc, [], 2) ); } @@ -182,9 +197,10 @@ public function testFoldrEmptyList() */ public function testFoldrDirectionIndependentNonEmptyList() { + $listOps = new ListOps(); $this->assertEquals( 15, - ListOps::foldr(static fn ($acc, $el) => $acc + $el, [1, 2, 3, 4], 5) + $listOps->foldr(static fn ($acc, $el) => $acc + $el, [1, 2, 3, 4], 5) ); } @@ -193,9 +209,10 @@ public function testFoldrDirectionIndependentNonEmptyList() */ public function testFoldrDirectionDependentNonEmptyList() { + $listOps = new ListOps(); $this->assertEquals( 9, - ListOps::foldr(static fn ($acc, $el) => $el / $acc, [1, 2, 3, 4], 24) + $listOps->foldr(static fn ($acc, $el) => $el / $acc, [1, 2, 3, 4], 24) ); } @@ -204,7 +221,8 @@ public function testFoldrDirectionDependentNonEmptyList() */ public function testReverseEmptyList() { - $this->assertEquals([], ListOps::reverse([])); + $listOps = new ListOps(); + $this->assertEquals([], $listOps->reverse([])); } /** @@ -212,7 +230,8 @@ public function testReverseEmptyList() */ public function testReverseNonEmptyList() { - $this->assertEquals([7, 5, 3, 1], ListOps::reverse([1, 3, 5, 7])); + $listOps = new ListOps(); + $this->assertEquals([7, 5, 3, 1], $listOps->reverse([1, 3, 5, 7])); } /** @@ -220,6 +239,7 @@ public function testReverseNonEmptyList() */ public function testReverseNonEmptyListIsNotFlattened() { - $this->assertEquals([[4, 5, 6], [], [3], [1, 2]], ListOps::reverse([[1, 2], [3], [], [4, 5, 6]])); + $listOps = new ListOps(); + $this->assertEquals([[4, 5, 6], [], [3], [1, 2]], $listOps->reverse([[1, 2], [3], [], [4, 5, 6]])); } } diff --git a/phpcs-php.xml b/phpcs-php.xml index 42a3cfa8..06d866c5 100644 --- a/phpcs-php.xml +++ b/phpcs-php.xml @@ -2,6 +2,8 @@ Coding standard for xPHP + + From 77e641b54ba7d2bfb43d014857735f16224a0fd3 Mon Sep 17 00:00:00 2001 From: homersimpsons Date: Sat, 17 Feb 2024 14:48:22 +0000 Subject: [PATCH 3/4] ListOps: Implement all tests and comment excluded tests --- exercises/practice/list-ops/.meta/tests.toml | 6 +++++ exercises/practice/list-ops/ListOpsTest.php | 24 +++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/exercises/practice/list-ops/.meta/tests.toml b/exercises/practice/list-ops/.meta/tests.toml index 08b1edc0..a0219b66 100644 --- a/exercises/practice/list-ops/.meta/tests.toml +++ b/exercises/practice/list-ops/.meta/tests.toml @@ -51,14 +51,17 @@ 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 +comment = "Re-implemented in 36549237-f765-4a4c-bfd9-5d3a8f7b07d2" [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 +comment = "Re-implemented in 7a626a3c-03ec-42bc-9840-53f280e13067" [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 +comment = "Re-implemented in d7fcad99-e88e-40e1-a539-4c519681f390" [36549237-f765-4a4c-bfd9-5d3a8f7b07d2] description = "folds (reduces) the given list from the left with a function -> empty list" @@ -75,14 +78,17 @@ 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 +comment = "Re-implemented in 17214edb-20ba-42fc-bda8-000a5ab525b0" [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 +comment = "Re-implemented in e1c64db7-9253-4a3d-a7c4-5273b9e2a1bd" [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 +comment = "Re-implemented in 8066003b-f2ff-437e-9103-66e6df474844" [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/ListOpsTest.php b/exercises/practice/list-ops/ListOpsTest.php index 337cf3d0..4d0f35ea 100644 --- a/exercises/practice/list-ops/ListOpsTest.php +++ b/exercises/practice/list-ops/ListOpsTest.php @@ -43,7 +43,7 @@ public function testAppendEmptyLists() } /** - * @testdox append entries to a list and return the new list -> empty list to list + * @testdox append entries to a list and return the new list -> list to empty list */ public function testAppendNonEmptyListToEmptyList() { @@ -51,6 +51,15 @@ public function testAppendNonEmptyListToEmptyList() $this->assertEquals([1, 2, 3, 4], $listOps->append([1, 2, 3, 4], [])); } + /** + * @testdox append entries to a list and return the new list -> empty list to list + */ + public function testAppendEmptyListToNonEmptyList() + { + $listOps = new ListOps(); + $this->assertEquals([1, 2, 3, 4], $listOps->append([], [1, 2, 3, 4])); + } + /** * @testdox append entries to a list and return the new list -> non-empty lists */ @@ -61,7 +70,7 @@ public function testAppendNonEmptyLists() } /** - * @testdox concat lists and lists of lists into new list -> empty list + * @testdox concatenate a list of lists -> empty list */ public function testConcatEmptyLists() { @@ -70,7 +79,7 @@ public function testConcatEmptyLists() } /** - * @testdox concat lists and lists of lists into new list -> list of lists + * @testdox concatenate a list of lists -> list of lists */ public function testConcatLists() { @@ -78,6 +87,15 @@ public function testConcatLists() $this->assertEquals([1, 2, 3, 4, 5, 6], $listOps->concat([1, 2], [3], [], [4, 5, 6])); } + /** + * @testdox concatenate a list of lists -> list of nested lists + */ + public function testConcatNestedLists() + { + $listOps = new ListOps(); + $this->assertEquals([[1], [2], [3], [], [4, 5, 6]], $listOps->concat([[1], [2]], [[3]], [[]], [[4, 5, 6]])); + } + /** * @testdox filter list returning only values that satisfy the filter function -> empty list */ From 69affc2575ead467a089a92899d28cd9a1b028c0 Mon Sep 17 00:00:00 2001 From: homersimpsons Date: Sat, 17 Feb 2024 17:56:25 +0000 Subject: [PATCH 4/4] ListOps: Use more expressive variable names --- exercises/practice/list-ops/ListOps.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/practice/list-ops/ListOps.php b/exercises/practice/list-ops/ListOps.php index 57510452..82eb363a 100644 --- a/exercises/practice/list-ops/ListOps.php +++ b/exercises/practice/list-ops/ListOps.php @@ -37,7 +37,7 @@ public function concat(array $list1, array ...$listn): array } /** - * @param callable(mixed $el): bool $predicate + * @param callable(mixed $item): bool $predicate */ public function filter(callable $predicate, array $list): array { @@ -50,7 +50,7 @@ public function length(array $list): int } /** - * @param callable(mixed $el): mixed $function + * @param callable(mixed $item): mixed $function */ public function map(callable $function, array $list): array { @@ -58,7 +58,7 @@ public function map(callable $function, array $list): array } /** - * @param callable(mixed $acc, mixed $el): mixed $function + * @param callable(mixed $accumulator, mixed $item): mixed $function */ public function foldl(callable $function, array $list, $accumulator) { @@ -66,7 +66,7 @@ public function foldl(callable $function, array $list, $accumulator) } /** - * @param callable(mixed $acc, mixed $el): mixed $function + * @param callable(mixed $accumulator, mixed $item): mixed $function */ public function foldr(callable $function, array $list, $accumulator) {