Added C-String support for 'is_palindrome'

Updated doc, example and tests.
This commit is contained in:
Alexander Zaitsev
2016-08-16 05:14:56 +03:00
parent 0f5136de65
commit 3c25ce1090
4 changed files with 103 additions and 25 deletions

View File

@ -9,23 +9,26 @@ Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
] ]
The header file 'is_palindrome.hpp' contains four variants of a single algorithm, is_palindrome. The header file 'is_palindrome.hpp' contains six variants of a single algorithm, is_palindrome.
The algorithm tests the sequence and returns true if the sequence is a palindrome; i.e, it is identical when traversed either backwards or frontwards. The algorithm tests the sequence and returns true if the sequence is a palindrome; i.e, it is identical when traversed either backwards or frontwards.
The routine `is_palindrome` takes a sequence and, optionally, a predicate. It will return true if the predicate returns true for tested elements by algorithm in the sequence. The routine `is_palindrome` takes a sequence and, optionally, a predicate. It will return true if the predicate returns true for tested elements by algorithm in the sequence.
The routine come in 4 forms; the first one takes two iterators to define the range. The second form takes two iterators to define the range and a predicate. The routine come in 6 forms; the first one takes two iterators to define the range. The second form takes two iterators to define the range and a predicate.
The third form takes a single range parameter, and uses Boost.Range to traverse it. And the fourth form takes a single range parameter ( uses Boost.Range to traverse it) and a predicate. The third form takes a single range parameter, and uses Boost.Range to traverse it. The fourth form takes a single range parameter ( uses Boost.Range to traverse it) and a predicate.
The fifth form takes a single C-string and a predicate. The sixth form takes a single C-string.
[heading interface] [heading interface]
The function `is_palindrome` returns true if the predicate returns true any tested by algorithm items in the sequence. The function `is_palindrome` returns true if the predicate returns true any tested by algorithm items in the sequence.
There are four versions: There are six versions:
1) takes two iterators. 1) takes two iterators.
2) takes two iterators and a predicate. 2) takes two iterators and a predicate.
3) takes a range. 3) takes a range.
4) takes a range and a predicate. 4) takes a range and a predicate.
5) takes a C-string and a predicate.
6) takes a C-string.
`` ``
template<typename BidirectionalIterator> template<typename BidirectionalIterator>
@ -36,6 +39,9 @@ template<typename Range>
bool is_palindrome ( const Range &r ); bool is_palindrome ( const Range &r );
template<typename Range, typename Predicate> template<typename Range, typename Predicate>
bool is_palindrome ( const Range &r, Predicate p ); bool is_palindrome ( const Range &r, Predicate p );
template<typename Predicate>
bool is_palindrome ( const char* str, Predicate p );
bool is_palindrome(const char* str);
`` ``
@ -59,6 +65,9 @@ is_palindrome(std::begin(oddNonPalindrome), std::end(oddNonPalindrome), funcComp
is_palindrome(std::begin(oddPalindrome), std::end(oddPalindrome)) --> true is_palindrome(std::begin(oddPalindrome), std::end(oddPalindrome)) --> true
is_palindrome(evenPalindrome, std::equal_to<int>())) --> true is_palindrome(evenPalindrome, std::equal_to<int>())) --> true
is_palindrome(std::begin(evenNonPalindrome), std::end(evenNonPalindrome)) --> false is_palindrome(std::begin(evenNonPalindrome), std::end(evenNonPalindrome)) --> false
is_palindrome(nullptr) --> true
is_palindrome("a") --> true
is_palindrome("aba", std::equal_to<char>()) --> true
`` ``
[heading Iterator Requirements] [heading Iterator Requirements]
@ -71,15 +80,15 @@ All of the variants of `is_palindrome` run in ['O(N)] (linear) time; that is, th
[heading Exception Safety] [heading Exception Safety]
All of the variants of `is_palindrome` take their parameters by value or const reference, and do not depend upon any global state. Therefore, all the routines in this file provide the strong exception guarantee. All of the variants of `is_palindrome` take their parameters by value, const pointer or const reference, and do not depend upon any global state. Therefore, all the routines in this file provide the strong exception guarantee.
[heading Notes] [heading Notes]
* `is_palindrome` returns true for empty ranges and for single element ranges. * `is_palindrome` returns true for empty ranges, null pointers and for single element ranges.
* If you use version of 'is_palindrome' without custom predicate, 'is_palindrome' uses default 'operator==' for elements. If you want use custom predicate, you must redefine 'operator=='. * If you use version of 'is_palindrome' without custom predicate, 'is_palindrome' uses default 'operator==()' for elements.
* Don't use 'const char*' with 'is_palindrome', because 'const char*' is always non-palindromic ('\0' at the end). If you will try to compile 'is_palindrome' with 'const char*', you will get an error. * Be careful with using not null pointer 'const char*' without '\0' - if you use it with 'is_palindrome', it's a undefined behaviour.
[endsect] [endsect]

View File

@ -90,5 +90,10 @@ int main ( int /*argc*/, char * /*argv*/ [] )
else else
std::cout << "This container is not palindrome" << std::endl; std::cout << "This container is not palindrome" << std::endl;
//You can use C-strings
if(ba::is_palindrome("aba"))
std::cout << "This C-string is palindrome" << std::endl;
else
std::cout << "This C-string is not palindrome" << std::endl;
return 0; return 0;
} }

View File

@ -17,6 +17,8 @@
#include <iterator> #include <iterator>
#include <functional> #include <functional>
#include <type_traits>
#include <cstring>
#include <boost/range/begin.hpp> #include <boost/range/begin.hpp>
#include <boost/range/end.hpp> #include <boost/range/end.hpp>
@ -34,7 +36,7 @@ namespace boost { namespace algorithm {
/// For other sequences function will return false. /// For other sequences function will return false.
/// Complexity: O(N). /// Complexity: O(N).
template <typename BidirectionalIterator, typename Predicate> template <typename BidirectionalIterator, typename Predicate>
bool is_palindrome(BidirectionalIterator begin, BidirectionalIterator end, Predicate p) bool is_palindrome(BidirectionalIterator begin, BidirectionalIterator end, Predicate p )
{ {
if(begin == end) if(begin == end)
{ {
@ -70,8 +72,26 @@ bool is_palindrome(BidirectionalIterator begin, BidirectionalIterator end, Predi
template <typename BidirectionalIterator> template <typename BidirectionalIterator>
bool is_palindrome(BidirectionalIterator begin, BidirectionalIterator end) bool is_palindrome(BidirectionalIterator begin, BidirectionalIterator end)
{ {
return is_palindrome(begin, end, if(begin == end)
std::equal_to<typename std::iterator_traits<BidirectionalIterator>::value_type> ()); {
return true;
}
--end;
while(begin != end)
{
if(!(*begin == *end))
{
return false;
}
++begin;
if(begin == end)
{
break;
}
--end;
}
return true;
} }
/// \fn is_palindrome ( const R& range ) /// \fn is_palindrome ( const R& range )
@ -82,7 +102,7 @@ bool is_palindrome(BidirectionalIterator begin, BidirectionalIterator end)
/// \note This function will return true for empty sequences and for palindromes. /// \note This function will return true for empty sequences and for palindromes.
/// For other sequences function will return false. /// For other sequences function will return false.
/// Complexity: O(N). /// Complexity: O(N).
template <typename R> template <typename R, typename std::enable_if<!std::is_integral<R>::value_type>::type>
bool is_palindrome(const R& range) bool is_palindrome(const R& range)
{ {
return is_palindrome(boost::begin(range), boost::end(range)); return is_palindrome(boost::begin(range), boost::end(range));
@ -97,26 +117,59 @@ bool is_palindrome(const R& range)
/// \note This function will return true for empty sequences and for palindromes. /// \note This function will return true for empty sequences and for palindromes.
/// For other sequences function will return false. /// For other sequences function will return false.
/// Complexity: O(N). /// Complexity: O(N).
template <typename R, typename Predicate> template <typename R, typename Predicate, typename std::enable_if<!std::is_integral<R>::value_type>::type>
bool is_palindrome(const R& range, Predicate p) bool is_palindrome(const R& range, Predicate p)
{ {
return is_palindrome(boost::begin(range), boost::end(range), p); return is_palindrome(boost::begin(range), boost::end(range), p);
} }
//Disable is_palindrome for const char* because it work not properly.
//Please use string_view for const char* cases.
//Here we use dirty hack to disable 'is_palindrome' with 'const char*'
//URL: http://stackoverflow.com/questions/14637356/static-assert-fails-compilation-even-though-template-function-is-called-nowhere
template<typename T>
struct foobar : std::false_type
{ };
template<typename T = int> /// \fn is_palindrome ( const char* str )
/// \return true if the entire sequence is palindrome
///
/// \param str C-string to be tested.
///
/// \note This function will return true for empty sequences and for palindromes.
/// For other sequences function will return false.
/// Complexity: O(N).
bool is_palindrome(const char* str) bool is_palindrome(const char* str)
{ {
static_assert(foobar<T>::value, "Using 'is_palindrome' for 'const char*' is dangerous, because result is always false" if(str == nullptr)
"(reason: '\0' in the end of the string). You can use string_view in this case"); {
return false; return true;
}
return is_palindrome(str, str + strlen(str));
}
/// \fn is_palindrome ( const char* str, Predicate p )
/// \return true if the entire sequence is palindrome
///
/// \param str C-string to be tested.
/// \param p A predicate used to compare the values.
///
/// \note This function will return true for empty sequences and for palindromes.
/// For other sequences function will return false.
/// Complexity: O(N).
template<typename Predicate>
bool is_palindrome(const char* str, Predicate p)
{
if(str == nullptr)
{
return true;
}
return is_palindrome(str, str + strlen(str), p);
}
bool is_palindrome (nullptr_t)
{
return true;
}
template<typename T>
bool is_palindrome (T)
{
return true;
} }
}} }}

View File

@ -47,6 +47,7 @@ void test_is_palindrome()
const int oddPalindrome[] = {1,2,3,2,1}; const int oddPalindrome[] = {1,2,3,2,1};
const int evenPalindrome[] = {1,2,2,1}; const int evenPalindrome[] = {1,2,2,1};
int evenNonPalindrome[] = {1,4,8,8}; int evenNonPalindrome[] = {1,4,8,8};
const char* stringNullPtr = nullptr;
// Test a default operator== // Test a default operator==
BOOST_CHECK ( ba::is_palindrome(empty)); BOOST_CHECK ( ba::is_palindrome(empty));
@ -60,6 +61,16 @@ void test_is_palindrome()
BOOST_CHECK ( ba::is_palindrome(empty.begin(), empty.end(), functorComparator())); BOOST_CHECK ( ba::is_palindrome(empty.begin(), empty.end(), functorComparator()));
BOOST_CHECK (!ba::is_palindrome(std::begin(oddNonPalindrome), std::end(oddNonPalindrome), funcComparator<int>)); BOOST_CHECK (!ba::is_palindrome(std::begin(oddNonPalindrome), std::end(oddNonPalindrome), funcComparator<int>));
BOOST_CHECK ( ba::is_palindrome(evenPalindrome, std::equal_to<int>())); BOOST_CHECK ( ba::is_palindrome(evenPalindrome, std::equal_to<int>()));
//Test C-strings like cases
BOOST_CHECK ( ba::is_palindrome(nullptr));
BOOST_CHECK ( ba::is_palindrome(0));
BOOST_CHECK ( ba::is_palindrome(stringNullPtr));
BOOST_CHECK ( ba::is_palindrome(""));
BOOST_CHECK ( ba::is_palindrome("a"));
BOOST_CHECK ( ba::is_palindrome("abacaba"), std::equal_to<char>());
BOOST_CHECK ( ba::is_palindrome("abba"));
BOOST_CHECK ( ba::is_palindrome("acab"));
} }
BOOST_AUTO_TEST_CASE( test_main ) BOOST_AUTO_TEST_CASE( test_main )