Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ std::string reverse_string(std::string_view original)
return std::string(rbegin(original), rend(original));
}

} // namespace reverse_string;
} // namespace reverse_string
```

## The type of the parameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ std::string reverse_string(std::string_view original)
return std::string{original};

// "divide and conquer" by reversing the two halfs of the string separately
auto half_size = size(str) / 2;
return reverse_string(original.substr(half_size) +
reverse_string(original.substr(0, half_size);
auto half_size = size(original) / 2;
return reverse_string(original.substr(half_size)) +
reverse_string(original.substr(0, half_size));
}

} // namespace reverse_string;
} // namespace reverse_string
```

## The type of the parameter
Expand Down Expand Up @@ -75,7 +75,7 @@ We divide the problem into smaller problems, then we "conquer" (solve) them inde

Calculating the `half_size` and splitting the string into two parts takes constant time (due to the `std::string_view`).
Each recursive function call handles one half of the string.
Combining the two reversed half at the end takes linear time.
Combining the two reversed halves at the end takes linear time.
Therefore the runtime complexity of this implementation is O(n log n), it is "linearithmic" (see the [Wikipedia][wiki-quasilinear-time]).

Note that in contrast to the [Linear Recursion][approach-linear-recursion] this approach is safe.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
if (size(original) <= 1)
return std::string{original};
auto half_size = size(str) / 2;
return reverse_string(original.substr(half_size) +
reverse_string(original.substr(0, half_size);
auto half_size = size(original) / 2;
return reverse_string(original.substr(half_size)) +
reverse_string(original.substr(0, half_size));
8 changes: 4 additions & 4 deletions exercises/practice/reverse-string/.approaches/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ void recursive_helper(std::string_view input, std::string& output)
return;

// append the last character of the input to the output
output.append(input.back());
output.push_back(input.back());
// and remove that last character from the input
input.remove_suffix(1);

Expand Down Expand Up @@ -194,9 +194,9 @@ std::string reverse_string(std::string_view original)
return std::string{original};

// "divide and conquer" by reversing the two halfs of the string separately
auto half_size = size(str) / 2;
return reverse_string(original.substr(half_size) +
reverse_string(original.substr(0, half_size);
auto half_size = size(original) / 2;
return reverse_string(original.substr(half_size)) +
reverse_string(original.substr(0, half_size));
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ std::string reverse_string(const std::string& original)
std::size_t rindex = size(original) - i - 1;

// (4) append each character (starting from the end)
result.append(original[rindex]);
result.push_back(original[rindex]);
}

// result now contains a reversed version of the string
return result;
}

} // namespace reverse_string;
} // namespace reverse_string
```

## The type of the parameter
Expand All @@ -47,7 +47,7 @@ The function takes its argument "by reference to const" (`const std::string&`).
That avoids creating an unnecessary copy which can be comparatively expensive for a `std::string`.
This is a best practice and widely recommended if a copy is expensive and the function does not need a "working copy" that it can modify (see the [C++ Core Guidelines][cgl-by-value-by-const-ref].)

Alternatively you could use a `std::string_view`.
Alternatively we could use a `std::string_view`.
That type was added to C++17, it is an immutable view of a string, it is cheap to construct and to copy (see [cppreference.com][cppref-stringview].)

## Constructing an empty `std::string`
Expand Down Expand Up @@ -84,8 +84,8 @@ for (auto rit = rbegin(original); rit != rend(original); ++rit)

## Appending to the result

The solution above uses the member function `append` (see [cppreference.com][cppref-string-append].)
There are other equivalent member function like `push_back()` (see [cppreference.com][cppref-string-pushback]) or the operator `+=` (see [cppreference.com][cppref-string-operator-plus-equal].)
The solution above uses the member function `push_back` (see [cppreference.com][cppref-string-pushback].)
Alternatives include the operator `+=` (see [cppreference.com][cppref-string-operator-plus-equal]) or the member function `append()` (with the count of `1` as an additional argument, see [cppreference.com][cppref-string-append].)
Either one is fine, chosing one is a matter of personal preferences.

## Variation: With assignments
Expand Down Expand Up @@ -116,7 +116,7 @@ for (std::size_t i = 0, rindex = size(original) - 1; i < size(original); ++i, --
This is a valid approach, it produces a correct result and is reasonably efficient.

But most experts recommend not doing things "manually" if there are better alternatives.
You could consider using the `std::string` class to construct the reversed string directly.
We could consider using the `std::string` class to construct the reversed string directly.
That would shorten the code, would be concise, easy to read, idiomatic, and very efficient.
Check out the [Direct Construction][approach-direct-construction] approach.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ std::string result;
for (std::size_t i = 0; i < size(original); ++i)
{
std::size_t rindex = size(original) - i - 1;
result.append(original[rindex]);
result.push_back(original[rindex]);
}
return result;
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void recursive_helper(std::string_view input, std::string& output)
return;

// (4) append the last character of the input to the output
output.append(input.back());
output.push_back(input.back());
// and "remove" the last character from the input
input.remove_suffix(1);

Expand All @@ -43,7 +43,7 @@ std::string reverse_string(std::string_view original)
return result;
}

} // namespace reverse_string;
} // namespace reverse_string
```

## The types of the parameters
Expand All @@ -66,8 +66,8 @@ In that case nothing needs to be done.

`std::string_view` has the member function `back` that returns the last character (see [cppreference.com][cppref-stringview-back]).

`std::string` has the member function `append` that appends something to the string (see [cppreference.com][cppref-string-append]).
There are equivalent member functions like `push_back` (see [cppreference.com][cppref-string-pushback]) or the operator `+=` (see [cppreference.com][cppref-string-operator-plus-assign]).
`std::string` has the member function `push_back` that appends a character to the string (see [cppreference.com][cppref-string-pushback]).
There are equivalent member functions like `append` (with the count as an additional argument, see [cppreference.com][cppref-string-append]) or the operator `+=` (see [cppreference.com][cppref-string-operator-plus-assign]).
In this case they do exactly the same.

`std::string_view` has the member function `remove_suffix` that *conceptually* removes some characteas from the end of the `std::string_view`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ void recursive_helper(std::string_view input, std::string& output)
{
if (input.empty())
return;
output.append(input.back());
output.push_back(input.back());
input.remove_suffix(1);
recursive_helper(input, output);
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ std::string reverse_string(const char *str)
return reverse_string(std::string_view{str});
}

} // namespace reverse_string;
} // namespace reverse_string
```

## The `[[nodiscard]]` attribute
Expand All @@ -60,10 +60,10 @@ That is helpful in situations where either the result contains some error code t

## Overloading functions

In C++ it is possible (and quite common) to define multiple different functions with the same name but different parameter (different number of parameter or different parameter types.)
When an overloaded function is called the compiler collects all candidate with that name, trims the set of candidate to the *viable* ones, and then selects the best fit for the function call (see [cppreference.com][cppref-overload-resolution]).
In C++ it is possible (and quite common) to define multiple functions with the same name but different parameters (different number of parameters or different parameter types.)
When an overloaded function is called the compiler collects all candidates with that name, trims the set of candidates to the *viable* ones, and then selects the best fit for the function call (see [cppreference.com][cppref-overload-resolution]).

Overloading function is often done if these functions perform the same task *conceptually* but in a slightly different way, depending on the arguments.
Overloading functions is often done if these functions perform the same task *conceptually* but in a slightly different way, depending on the arguments.

## Handling a `std::string` rvalue

Expand All @@ -80,7 +80,7 @@ In that case the program does not have to allocate memory for the reversed versi

The first overload (that takes an rvalue reference) does not allocate any memory dynamically, we know for sure that no exceptions can occur.
With `noexcept` that can be formally specified (see [cppreference.com][cppref-noexcept].)
That can allows the compiler to generate better instructions when the function is called.
That can allow the compiler to generate better instructions when the function is called.

## Calling `std::reverse`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ std::string reverse_string(std::string_view original)
return result;
}

} // namespace reverse_string;
} // namespace reverse_string
```

## The type of the parameter
Expand Down Expand Up @@ -68,7 +68,7 @@ In programming and computer science "stack" is a term for a container that suppo
- You can *pop* the top-most (i.e. most recently pushed) item off the stack.
- You can *peek* at the top-most item (without popping it off the stack).

`std::string_view` have the member functions `back` (see [cppreference.com][cppref-stringview-back]) and `remove_suffix` (see [cppreference.com][cppref-string-removesuffix]).
`std::string_view` have the member functions `back` (see [cppreference.com][cppref-stringview-back]) and `remove_suffix` (see [cppreference.com][cppref-stringview-removesuffix]).
`std::string` has the member function `push_back` (see [cppreference.com][cppref-string-pushback]).
They let use use the two strings like stacks.

Expand All @@ -87,5 +87,5 @@ It is mostly presented as an interesting idea and unique way to think about the
[cppref-stringview]: https://en.cppreference.com/w/cpp/string/basic_string_view
[wiki-stack]: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)
[cppref-stringview-back]: https://en.cppreference.com/w/cpp/string/basic_string_view/back
[cppref-string-removesuffix]: https://en.cppreference.com/w/cpp/string/basic_string_view/remove_suffix
[cppref-stringview-removesuffix]: https://en.cppreference.com/w/cpp/string/basic_string_view/remove_suffix
[cppref-string-pushback]: https://en.cppreference.com/w/cpp/string/basic_string/push_back
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ std::string reverse_string(std::string str)
return str;
}

} // namespace reverse_string;
} // namespace reverse_string
```

## The type of the parameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ std::string reverse_string(std::string str)
return str;
}

} // namespace reverse_string;
} // namespace reverse_string
```

## Including the header
Expand Down
Loading