Skip to content

Commit b80fc00

Browse files
Add ListOps exercise (#605)
* Add ListOps exercise * Explain `callables` in PHP * ListOps: Implement all tests from problem spec
1 parent 2235dfd commit b80fc00

File tree

9 files changed

+614
-0
lines changed

9 files changed

+614
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,14 @@
932932
"practices": [],
933933
"prerequisites": [],
934934
"difficulty": 2
935+
},
936+
{
937+
"slug": "list-ops",
938+
"name": "List Ops",
939+
"uuid": "56f36086-96a8-4941-8c46-b563f02e5b09",
940+
"practices": [],
941+
"prerequisites": [],
942+
"difficulty": 6
935943
}
936944
]
937945
},
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
## Callable
2+
3+
In PHP there is a concept of [callable](https://www.php.net/manual/en/language.types.callable.php).
4+
5+
Those can take multiple forms, but we will focus on [anonymous functions](https://www.php.net/manual/en/functions.anonymous.php).
6+
7+
It is possible to create an anonymous function in a variable and call it with parameters:
8+
9+
```php
10+
$double = function ($number) {
11+
return $number * 2;
12+
}
13+
14+
$double(2); // returns 4
15+
$double(4); // returns 8
16+
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Instructions
2+
3+
Implement basic list operations.
4+
5+
In functional languages list operations like `length`, `map`, and `reduce` are very common.
6+
Implement a series of basic list operations, without using existing functions.
7+
8+
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:
9+
10+
- `append` (_given two lists, add all items in the second list to the end of the first list_);
11+
- `concatenate` (_given a series of lists, combine all items in all lists into one flattened list_);
12+
- `filter` (_given a predicate and a list, return the list of all items for which `predicate(item)` is True_);
13+
- `length` (_given a list, return the total number of items within it_);
14+
- `map` (_given a function and a list, return the list of the results of applying `function(item)` on all items_);
15+
- `foldl` (_given a function, a list, and initial accumulator, fold (reduce) each item into the accumulator from the left_);
16+
- `foldr` (_given a function, a list, and an initial accumulator, fold (reduce) each item into the accumulator from the right_);
17+
- `reverse` (_given a list, return a list with all the original items, but in reversed order_).
18+
19+
Note, the ordering in which arguments are passed to the fold functions (`foldl`, `foldr`) is significant.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"authors": ["homersimpsons"],
3+
"files": {
4+
"solution": [
5+
"ListOps.php"
6+
],
7+
"test": [
8+
"ListOpsTest.php"
9+
],
10+
"example": [
11+
".meta/example.php"
12+
]
13+
},
14+
"blurb": "Implement basic list operations."
15+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
/*
4+
* By adding type hints and enabling strict type checking, code can become
5+
* easier to read, self-documenting and reduce the number of potential bugs.
6+
* By default, type declarations are non-strict, which means they will attempt
7+
* to change the original type to match the type specified by the
8+
* type-declaration.
9+
*
10+
* In other words, if you pass a string to a function requiring a float,
11+
* it will attempt to convert the string value to a float.
12+
*
13+
* To enable strict mode, a single declare directive must be placed at the top
14+
* of the file.
15+
* This means that the strictness of typing is configured on a per-file basis.
16+
* This directive not only affects the type declarations of parameters, but also
17+
* a function's return type.
18+
*
19+
* For more info review the Concept on strict type checking in the PHP track
20+
* <link>.
21+
*
22+
* To disable strict typing, comment out the directive below.
23+
*/
24+
25+
declare(strict_types=1);
26+
27+
class ListOps
28+
{
29+
public function append(array $list1, array $list2): array
30+
{
31+
foreach ($list2 as $el) {
32+
$list1[] = $el;
33+
}
34+
return $list1;
35+
}
36+
37+
public function concat(array $list1, array ...$listn): array
38+
{
39+
foreach ($listn as $list) {
40+
$list1 = self::append($list1, $list);
41+
}
42+
return $list1;
43+
}
44+
45+
public function filter(callable $predicate, array $list): array
46+
{
47+
$result = [];
48+
foreach ($list as $el) {
49+
if ($predicate($el)) {
50+
$result[] = $el;
51+
}
52+
}
53+
return $result;
54+
}
55+
56+
public function length(array $list): int
57+
{
58+
$count = 0;
59+
foreach ($list as $_el) {
60+
$count++;
61+
}
62+
return $count;
63+
}
64+
65+
public function map(callable $function, array $list): array
66+
{
67+
$result = [];
68+
foreach ($list as $el) {
69+
$result[] = $function($el);
70+
}
71+
return $result;
72+
}
73+
74+
public function foldl(callable $function, array $list, $accumulator)
75+
{
76+
foreach ($list as $el) {
77+
$accumulator = $function($accumulator, $el);
78+
}
79+
return $accumulator;
80+
}
81+
82+
public function foldr(callable $function, array $list, $accumulator)
83+
{
84+
while (self::length($list) > 0) {
85+
$el = array_pop($list);
86+
$accumulator = $function($accumulator, $el);
87+
}
88+
return $accumulator;
89+
}
90+
91+
public function reverse(array $list): array
92+
{
93+
$result = [];
94+
while (self::length($list) > 0) {
95+
$result[] = array_pop($list);
96+
}
97+
return $result;
98+
}
99+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[485b9452-bf94-40f7-a3db-c3cf4850066a]
13+
description = "append entries to a list and return the new list -> empty lists"
14+
15+
[2c894696-b609-4569-b149-8672134d340a]
16+
description = "append entries to a list and return the new list -> list to empty list"
17+
18+
[e842efed-3bf6-4295-b371-4d67a4fdf19c]
19+
description = "append entries to a list and return the new list -> empty list to list"
20+
21+
[71dcf5eb-73ae-4a0e-b744-a52ee387922f]
22+
description = "append entries to a list and return the new list -> non-empty lists"
23+
24+
[28444355-201b-4af2-a2f6-5550227bde21]
25+
description = "concatenate a list of lists -> empty list"
26+
27+
[331451c1-9573-42a1-9869-2d06e3b389a9]
28+
description = "concatenate a list of lists -> list of lists"
29+
30+
[d6ecd72c-197f-40c3-89a4-aa1f45827e09]
31+
description = "concatenate a list of lists -> list of nested lists"
32+
33+
[0524fba8-3e0f-4531-ad2b-f7a43da86a16]
34+
description = "filter list returning only values that satisfy the filter function -> empty list"
35+
36+
[88494bd5-f520-4edb-8631-88e415b62d24]
37+
description = "filter list returning only values that satisfy the filter function -> non-empty list"
38+
39+
[1cf0b92d-8d96-41d5-9c21-7b3c37cb6aad]
40+
description = "returns the length of a list -> empty list"
41+
42+
[d7b8d2d9-2d16-44c4-9a19-6e5f237cb71e]
43+
description = "returns the length of a list -> non-empty list"
44+
45+
[c0bc8962-30e2-4bec-9ae4-668b8ecd75aa]
46+
description = "return a list of elements whose values equal the list value transformed by the mapping function -> empty list"
47+
48+
[11e71a95-e78b-4909-b8e4-60cdcaec0e91]
49+
description = "return a list of elements whose values equal the list value transformed by the mapping function -> non-empty list"
50+
51+
[613b20b7-1873-4070-a3a6-70ae5f50d7cc]
52+
description = "folds (reduces) the given list from the left with a function -> empty list"
53+
include = false
54+
comment = "Re-implemented in 36549237-f765-4a4c-bfd9-5d3a8f7b07d2"
55+
56+
[e56df3eb-9405-416a-b13a-aabb4c3b5194]
57+
description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list"
58+
include = false
59+
comment = "Re-implemented in 7a626a3c-03ec-42bc-9840-53f280e13067"
60+
61+
[d2cf5644-aee1-4dfc-9b88-06896676fe27]
62+
description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list"
63+
include = false
64+
comment = "Re-implemented in d7fcad99-e88e-40e1-a539-4c519681f390"
65+
66+
[36549237-f765-4a4c-bfd9-5d3a8f7b07d2]
67+
description = "folds (reduces) the given list from the left with a function -> empty list"
68+
reimplements = "613b20b7-1873-4070-a3a6-70ae5f50d7cc"
69+
70+
[7a626a3c-03ec-42bc-9840-53f280e13067]
71+
description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list"
72+
reimplements = "e56df3eb-9405-416a-b13a-aabb4c3b5194"
73+
74+
[d7fcad99-e88e-40e1-a539-4c519681f390]
75+
description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list"
76+
reimplements = "d2cf5644-aee1-4dfc-9b88-06896676fe27"
77+
78+
[aeb576b9-118e-4a57-a451-db49fac20fdc]
79+
description = "folds (reduces) the given list from the right with a function -> empty list"
80+
include = false
81+
comment = "Re-implemented in 17214edb-20ba-42fc-bda8-000a5ab525b0"
82+
83+
[c4b64e58-313e-4c47-9c68-7764964efb8e]
84+
description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list"
85+
include = false
86+
comment = "Re-implemented in e1c64db7-9253-4a3d-a7c4-5273b9e2a1bd"
87+
88+
[be396a53-c074-4db3-8dd6-f7ed003cce7c]
89+
description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list"
90+
include = false
91+
comment = "Re-implemented in 8066003b-f2ff-437e-9103-66e6df474844"
92+
93+
[17214edb-20ba-42fc-bda8-000a5ab525b0]
94+
description = "folds (reduces) the given list from the right with a function -> empty list"
95+
reimplements = "aeb576b9-118e-4a57-a451-db49fac20fdc"
96+
97+
[e1c64db7-9253-4a3d-a7c4-5273b9e2a1bd]
98+
description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list"
99+
reimplements = "c4b64e58-313e-4c47-9c68-7764964efb8e"
100+
101+
[8066003b-f2ff-437e-9103-66e6df474844]
102+
description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list"
103+
reimplements = "be396a53-c074-4db3-8dd6-f7ed003cce7c"
104+
105+
[94231515-050e-4841-943d-d4488ab4ee30]
106+
description = "reverse the elements of the list -> empty list"
107+
108+
[fcc03d1e-42e0-4712-b689-d54ad761f360]
109+
description = "reverse the elements of the list -> non-empty list"
110+
111+
[40872990-b5b8-4cb8-9085-d91fc0d05d26]
112+
description = "reverse the elements of the list -> list of lists is not flattened"
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/*
4+
* By adding type hints and enabling strict type checking, code can become
5+
* easier to read, self-documenting and reduce the number of potential bugs.
6+
* By default, type declarations are non-strict, which means they will attempt
7+
* to change the original type to match the type specified by the
8+
* type-declaration.
9+
*
10+
* In other words, if you pass a string to a function requiring a float,
11+
* it will attempt to convert the string value to a float.
12+
*
13+
* To enable strict mode, a single declare directive must be placed at the top
14+
* of the file.
15+
* This means that the strictness of typing is configured on a per-file basis.
16+
* This directive not only affects the type declarations of parameters, but also
17+
* a function's return type.
18+
*
19+
* For more info review the Concept on strict type checking in the PHP track
20+
* <link>.
21+
*
22+
* To disable strict typing, comment out the directive below.
23+
*/
24+
25+
declare(strict_types=1);
26+
27+
class ListOps
28+
{
29+
public function append(array $list1, array $list2): array
30+
{
31+
throw new \BadMethodCallException("Implement the append function");
32+
}
33+
34+
public function concat(array $list1, array ...$listn): array
35+
{
36+
throw new \BadMethodCallException("Implement the concat function");
37+
}
38+
39+
/**
40+
* @param callable(mixed $item): bool $predicate
41+
*/
42+
public function filter(callable $predicate, array $list): array
43+
{
44+
throw new \BadMethodCallException("Implement the filter function");
45+
}
46+
47+
public function length(array $list): int
48+
{
49+
throw new \BadMethodCallException("Implement the length function");
50+
}
51+
52+
/**
53+
* @param callable(mixed $item): mixed $function
54+
*/
55+
public function map(callable $function, array $list): array
56+
{
57+
throw new \BadMethodCallException("Implement the map function");
58+
}
59+
60+
/**
61+
* @param callable(mixed $accumulator, mixed $item): mixed $function
62+
*/
63+
public function foldl(callable $function, array $list, $accumulator)
64+
{
65+
throw new \BadMethodCallException("Implement the foldl function");
66+
}
67+
68+
/**
69+
* @param callable(mixed $accumulator, mixed $item): mixed $function
70+
*/
71+
public function foldr(callable $function, array $list, $accumulator)
72+
{
73+
throw new \BadMethodCallException("Implement the foldr function");
74+
}
75+
76+
public function reverse(array $list): array
77+
{
78+
throw new \BadMethodCallException("Implement the reverse function");
79+
}
80+
}

0 commit comments

Comments
 (0)