Learning recursion: How can I locate a substring index within a string without using find? - c++

I have a recursive function to find the starting index of a substring within a string. I am learning to use recursion, so the find function is not allowed. I believe I have met most of the conditions. This function is supposed to find the correct index in the string. If it is blank it returns -1.
Here is the real problem. If I enter a string "nothing" and search for "jax" it doesn't return -1. I don't understand why. Any help please? Here is the code:
The user would enter string s and t passed into below:
int index_of(string s, string t)
{
int start = 0;
int len2 = t.length();
int index = 0;
if (s == "")
{
return -1;
}
else if (s.substr(1).length() <= t.length())
{
return -1;
}
else if ( s.substr(start, len2) == t)
{
return index;
}
else
{
index ++;
return index + index_of(s.substr(1), t);
}
return -1;
}

There are several problems -- some minor ones, and some quite important ones.
You have two variables, start and index, to indicate "the current position", but one would be enough.
index can only be 0 or 1. Therefore, the way it is currently written, you could easily get rid of index and start altogether.
Important: When, during the final recursion, the end of the string is reached, you return -1 to the previous recursive call. Then, because of the way the recursive calls are done, you add 1 and return that to the previous call, and so forth. The value finally returned is the -1 plus the length of the string. That is why you get strange results.
This comparison
if (s.substr(1).length() <= t.length())
does not make much sense.
Taking all of this into account, here is an improved version:
#include <iostream>
#include <string>
int index_of(
const std::string &s,
const std::string &t,
const size_t index)
{
int len2 = t.length();
if ((s.length() - index) < t.length())
return -1;
else if (s.substr(index,len2) == t)
return index;
else
return index_of(s,t,index + 1);
return -1;
}
/** Overloading, so you can call index_of with just
two arguments */
int index_of(const std::string &s, const std::string &t)
{
return index_of(s,t,0);
}
/** Some test cases. */
int main()
{
std::cout << index_of("hello","ello") << std::endl;
std::cout << index_of("nothing","jax") << std::endl;
std::cout << index_of("hello","llo") << std::endl;
std::cout << index_of("hello","lo") << std::endl;
std::cout << index_of("hello","o") << std::endl;
std::cout << index_of("hello","hel") << std::endl;
}

The best way to learn how to debug problems like this is to work them out on paper. Your example is small enough that it shouldn't take too long. It's pretty clear that you're going to fall into your else case in the first few steps because the strings don't match. So we have:
index_of("nothing", "jax"):
index++; // index is now 1
return 1 + index_of("othing", "jax");
index_of("othing", "jax"):
index++; // index is now 1
return 1 + index_of("thing", "jax");
etc.
Does that help?

Related

C++ “add explicit braces to avoid dangling else”

bool isPalindromeIterative(const char *s1){
int len=strlen(s1)-1;
if(len>0)
if(s1[0]==s1[len]){
len-=2;
isPalindromeIterative(s1+1);
}
else
return false;
return true;
}
I am writing about Palindrome.
And when I run it, it appears warning like this:
":79:13: warning: add explicit braces to avoid dangling else
[-Wdangling-else]"
Please help me ! Thanks!
There is formally nothing wrong with the code, unless you wanted the else to match the outer if. A common mistake.
If you add braces everywhere, it will be clear what you intended:
if(len>0)
{
if(s1[0]==s1[len])
{
len-=2;
isPalindromeIterative(s1+1);
}
else
{
return false;
}
}
When you write,
if(len>0)
if(s1[0]==s1[len]){
// This has no effect on the recursive call.
len-=2;
// You missed the return in your post.
return isPalindromeIterative(s1+1);
}
else
return false;
it's most likely that you meant to associate the else with the second if.
if(len>0)
{
if(s1[0]==s1[len])
{
return isPalindromeIterative(s1+1);
}
else
return false;
}
However, the compiler does not use the indent to figure that out. It's possible that, from the compiler writer's point of view, you meant to associate the else with the first if:
if(len>0)
{
if(s1[0]==s1[len])
{
return isPalindromeIterative(s1+1);
}
}
else
{
return false;
}
Since this is common mistake made by developers, the compiler warns you and hopes that you will update the code so that it's less likely to turn into a runtime error.
I want to point out that the recursion logic you are using to detect whether a string is palindrome is wrong.
Let's say your string is "abxba".
In the first iteration, you compare 'a' with 'a'.
In the next iteration, you compare 'b' with 'a', which is incorrect. You end up with the wrong answer.
You have to change your strategy a bit. Use:
bool isPalindromeIterative(const char *s1, int start, int end)
{
if ( start >= end )
{
return true;
}
if ( s1[start] == s1[end] )
{
return isPalindromeIterative(s1, start+1, end-1)
}
return false;
}
The start of the iterative call has to be made as:
isPalindromeIterative(s1, 0, strlen(s1)-1);
It is a style warning against reading the if clauses wrong.
if(len>0) {
if(s1[0]==s1[len]){
len-=2;
isPalindromeIterative(s1+1);
}
} else {
return false;
}
is better to read and less error-prone.
We have similar coding guidelines in our company; for an if, which has bracket clauses, all else branches and all other ifs within the if of the highest order have to have brackets.
Else your example could too easily be misread as e.g.
if(len>0)
if(s1[0]==s1[len]){
len-=2;
isPalindromeIterative(s1+1);
}
else
return false;
Your original post headline mentioned iterative, and 'Iterative' is still part of your function name (even though it is recursive).
You marked this post as c++, but did not use classes.
The other answers addressed your specific questions about your error messages.
For your consideration, and because you have already selected a recursive answer, here is a possible C++ iterative solution and tail-recursive solution using std::string&.
#include <iostream>
#include <string>
class T589_t
{
public:
int exec(int argc, char* argv[])
{
if (argc < 2)
{
std::cerr << "\n please provide one or more string(s) to test"
<< std::endl;
return -1;
}
for (int i = 0; i < argc; ++i)
{
std::string s = argv[i];
{
std::string yesno = (isPalindromeIterative(s) ? " is" : " is not");
std::cout << "\n '" << s << "'" << yesno << " a palindrome (iterative)" << std::flush;
}
std::cout << std::endl;
{
std::string yesno = (isPalindromeRecursive(s) ? " is" : " is not");
std::cout << " '" << s << "'" << yesno << " a palindrome (recursive)" << std::endl;
}
} // for 0..argc
return 0;
}
private: // methods
bool isPalindromeIterative(const std::string& s1)
{ // ^^^^^^^^^
bool retVal = false; // guess s1 is not palindrome
int left = 0; // index of left most char
int right = static_cast<int>(s1.size()) - 1; // index of right most char
do { // iterative loop
if (s1[left] != s1[right]) break; // when not equal, is not palindrome
left += 1; right -= 1; // iterate!
if (left >= right) // all chars tested?
{
retVal = true; // confirm palindrome
break; // exit
}
} while (true);
return retVal;
}
// Notes './dumy589' // odd length 9
// ^-------^ [0] vs [8]
// ^-----^ [1] vs [7]
// ^---^ [2] vs [6]
// ^-^ [3] vs [5]
// ^ [4] == [4] // left >= right, break
// Notes 'abccba' // even length 6
// ^----^ [0] vs [5]
// ^--^ [1] vs [4]
// ^^ [2] vs [3]
// [3] vs [2] // left >= right, break
// Notes 'abcba' // odd length 5
// ^---^ [0] vs [4]
// ^-^ [1] vs [3]
// ^ [2] vs [2] // left >= right, break
// and bonus: tail recursion based on above iterative
// vvvvvvvvv
bool isPalindromeRecursive(const std::string& s1)
{
// index of left most char
int left = 0;
// index of right most char
int right = static_cast<int>(s1.size()) - 1;
return (isPalindromeRecursive(s1, left, right));
}
bool isPalindromeRecursive(const std::string& s1, int left, int right)
{
if (s1[left] != s1[right]) return false;
left += 1; right -= 1;
if ( left >= right ) return true;
return (isPalindromeRecursive(s1, left, right));
}
}; // class T589_t
int main(int argc, char* argv[])
{
T589_t t589;
return t589.exec(argc, argv);
}
On Linux, argv[0] is the executable name.
environment:
Lubuntu 17.10,
g++ (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0
With invocation:
./dumy589 aba abccba tx s
This code reports:
'./dumy589' is not a palindrome (iterative)
'./dumy589' is not a palindrome (recursive)
'aba' is a palindrome (iterative)
'aba' is a palindrome (recursive)
'abccba' is a palindrome (iterative)
'abccba' is a palindrome (recursive)
'tx' is not a palindrome (iterative)
'tx' is not a palindrome (recursive)
's' is a palindrome (iterative)
's' is a palindrome (recursive)

How can I trace back the error

I was assigned to create an array check (to see if the array is increasing, decreasing, or neither [then exiting if neither]) and a recursive binary search for one of my assignments. I was able to do these things after some help from my peers, but I need help in finding what seems to be causing the error
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
Aborted
when running the code. I Googled this error and this error seems to be vague or I just am not understanding. It compiles without errors, but I need help in what finding what I did wrong. It is able to run without the binarySearchR function and its associating code, as the array check on its own was the previous assignment. Below is the code, and I thank you so much in advance!
#include <iosteam>
#include <string>
#include <cstdlib>
#include <fstream>
using namespace std;
int checkArraySort (string *fileLines, int numberOfLines);
int binarySearchR (string *fileLines, string searchKey, int iMin, int iMax);
int main ()
{
int numberOfLines = 0;
string searchKey = 0;
cout << "Input search key: ";
cin >> searchKey;
ifstream fileIn;
fileIn.open("words_in.txt");
string line;
if (fileIn.eof()) /* Checks file to see if it is blank before proceeding */
{
exit (EXIT_SUCCESS);
}
else
{
while(!(fileIn.eof()))
{
fileIn >> line;
numberOfLines++;
}
fileIn.close(); /* closes fileIn, need to reopen to reset the line location */
fileIn.open("words_in.txt");
string *fileInLines;
fileInLines = new string[numberOfLines];
for (int i = 0; i < numberOfLines; i++)
{
fileIn >> line;
fileInLines[i] = line;
}
fileIn.close(); /* closes fileIn */
int resultingCheck = checkArraySort(fileInLines, numberOfLines);
if (resultingCheck == -1)
{
cout << "The array is sorted in descending order." << endl;
}
else if (resultingCheck == 1)
{
cout << "The array is sorted in ascending order." << endl;
}
else
{
cerr << "ERROR: Array not sorted!" << endl;
exit (EXIT_FAILURE);
}
int searchResult = binarySearchR (fileInLines, searchKey, 0, numberOfLines);
if (!searchResult == -1)
{
cout << "Key found at index " << searchResult << "." << endl;
}
else
{
cout << "Key not found at any index." << endl;
}
exit (EXIT_SUCCESS);
}
}
int checkArraySort (string *fileLines, int numberOfLines)
{
int result = 1; /* Ascending by default */
for (int i = 1; i < numberOfLines; i++) /* Checks if decending */
{
if (fileLines[i] < fileLines[i-1])
{
result = -1;
}
}
if (result == -1) /* Makes sure it is descending (or if it is neither) */
{
for (int i = 1; i < numberOfLines; i++)
{
if (fileLines[i] > fileLines[i-1])
{
result = 0;
}
}
}
return result;
}
int binarySearchR (string *fileLines, string searchKey, int iMin, int iMax)
{
// so, its gotta look at the center value and each times, it discards half of the remaining list.
if (iMax < iMin) /* If the minimum is greater than the maximum */
{
return -1;
}
else
{
int iMid = (iMin + iMax) / 2;
if (fileLines[iMid] > searchKey) /* If the key is in the lower subset */
{
return binarySearchR (fileLines, searchKey, iMin, iMid - 1);
}
else if (fileLines[iMid] < searchKey) /*If the key is in the upper subset */
{
return binarySearchR (fileLines, searchKey, iMin, iMid + 1);
}
else /*If anything else besides the two */
{
return iMid;
}
}
}
The easy way: add a bunch of cout s to see where you program goes and what the values are.
Pros
Easy to do
Cons
Requires a recompile each time you want to add more info
The hard way: Learn to use a debugger
Pros
Can inspect "on the fly"
Don't need to rebuild
Can use what you learn in every other C++ program
Cons
Requires a bit of research to learn how to do it.

The Difference Between Calling and Returning a Recursive Function in C++

Can someone please explain with a simple example what precisely is the difference between calling and returning a recursive function in c++?
Here is my code which seeks to find a character in a string recursively. It works fine when I just call find(); the function displays an integer value, But, when I code return find(letter,word), it gives the correct result as either a 1 or 0. Thanks
bool find(char f,string str)
{
static int len = str.length() - 1;
static int count = 1;
if (len<0)
{
return false;
}
else
{
if (str[len] == f)
{
return true;
}
else
{
len--;
return find(f, str);
}
}
}
You don't return the function you return the result of the call.
Here a basic example of recursion :
unsigned int factorial(unsigned int n)
{
if (n == 0)
return 1;
return n * factorial(n - 1);
}
If we call the function with n == 2 the program will do:
1) factorial (2) => return 2 * factorial(1); // It calls factorial with n == 1, do the multiplication and then return the result.
2) factorial(1) => return 1 * factorial(0); // same here with n == 0
3) factorial(0) => return 1; // from here the program will come back from the calls with the results
4) factorial(1) => return 1 * 1; => return 1;
5) factorial(2) => return 2 * 1; => return 2;
Few other things : be aware that your code will only work once as you use static int len, and count is a useless variable.
The function signature indicates a return value, so you need to embed the function call in a return statement for a value to be returned by the function, especially when neither of the if-conditionals are true.
Consider the following code (a variant of what the OP provided):
#include <iostream>
using namespace std;
int find(char f,string str) {
static int len = str.length() - 1;
static int count = 1;
int temp = 0;
if (len < 0) {
cout << count << "\n";
return -99;
}
else
if ( str[len] == f) {
return len;
}
len--;
count++;
temp = find( f, str );
cout << temp << "\n";
return temp;
}
int main() {
char ch = 'z';
int res = find(ch,"I");
if (res < 0) {
cout << "Letter '" << ch << "' was not found";
}
return 0;
}
See demo
Note that find() has a return value of -99 when the letter is not found. That value is captured by temp in find() and the function then returns the value of temp.
Now, consider main() -- its signature also indicates a return value, although it is discarded. If you attempt to execute the code without returning some kind of an integer in main(), the execution will be flawed.
So, whether you use a function recursively or not, if the function's signature indicates a return value then you need to return a value of the expected type. Since find() may return -99 or the position of the found letter, when it executes it will evaluate as one of those two values which will be returned by means of the return statement.

LuaPlus: Register function with paramers and one return value?

I don't really understand the system. Using .RegisterDirect("myFunc",myFunc) I can register a function that can't take parameters, but therefore can return a value.
Using .Register("myFunc",myFunc) I can register a function that can take parameters, but therefore can't return any values.
I literally spent days on this issue now and I just can't figure it out. I would really appreciate it if someone would take a look at this.
Here is the documentation. Here is a quick example of how Register and RegisterDirect. Let's say I wanted the Print function to always return the string "hello". How would I do that?
#include "stdafx.hpp"
int Print(LuaPlus::LuaState* pState) {
int top = pState->GetTop();
std::stringstream output;
for( int i = 1; i <= top; ++i ) {
output << pState->CheckString(i) << std::endl;
}
std::cout << output.str();
return 0; // We don't return any values to the script
}
int Get2222() {
return 2222;
}
int main() {
LuaPlus::LuaState* pState = LuaPlus::LuaState::Create( true );
LuaPlus::LuaObject globals = pState->GetGlobals();
globals.Register("Print",Print);
globals.RegisterDirect("Get2222",Get2222);
char pPath[ MAX_PATH ];
GetCurrentDirectory(MAX_PATH,pPath);
strcat_s(pPath,MAX_PATH,"\\test.lua");
if( pState->DoFile(pPath) ) {
// An error occured
if( pState->GetTop() == 1 )
std::cout << "An error occured: " << pState->CheckString(1) << std::endl;
}
LuaPlus::LuaState::Destroy( pState );
pState = nullptr;
getchar();
return 0;
}
Afaik I would have to push the value on the stack and return 1 to indicate that there is something on the stack. But that didn't seem work. I tried to add this to the function to return the number 4:
pState->PushInteger(4);
return 1;
I really hope you can help me out here.

Check if element found in array c++

How can I check if my array has an element I'm looking for?
In Java, I would do something like this:
Foo someObject = new Foo(someParameter);
Foo foo;
//search through Foo[] arr
for(int i = 0; i < arr.length; i++){
if arr[i].equals(someObject)
foo = arr[i];
}
if (foo == null)
System.out.println("Not found!");
else
System.out.println("Found!");
But in C++ I don't think I'm allowed to search if an Object is null so what would be the C++ solution?
In C++ you would use std::find, and check if the resultant pointer points to the end of the range, like this:
Foo array[10];
... // Init the array here
Foo *foo = std::find(std::begin(array), std::end(array), someObject);
// When the element is not found, std::find returns the end of the range
if (foo != std::end(array)) {
cerr << "Found at position " << std::distance(array, foo) << endl;
} else {
cerr << "Not found" << endl;
}
You would just do the same thing, looping through the array to search for the term you want. Of course if it's a sorted array this would be much faster, so something similar to prehaps:
for(int i = 0; i < arraySize; i++){
if(array[i] == itemToFind){
break;
}
}
There are many ways...one is to use the std::find() algorithm, e.g.
#include <algorithm>
int myArray[] = { 3, 2, 1, 0, 1, 2, 3 };
size_t myArraySize = sizeof(myArray) / sizeof(int);
int *end = myArray + myArraySize;
// find the value 0:
int *result = std::find(myArray, end, 0);
if (result != end) {
// found value at "result" pointer location...
}
Here is a simple generic C++11 function contains which works for both arrays and containers:
using namespace std;
template<class C, typename T>
bool contains(C&& c, T e) { return find(begin(c), end(c), e) != end(c); };
Simple usage contains(arr, el) is somewhat similar to in keyword semantics in Python.
Here is a complete demo:
#include <algorithm>
#include <array>
#include <string>
#include <vector>
#include <iostream>
template<typename C, typename T>
bool contains(C&& c, T e) {
return std::find(std::begin(c), std::end(c), e) != std::end(c);
};
template<typename C, typename T>
void check(C&& c, T e) {
std::cout << e << (contains(c,e) ? "" : " not") << " found\n";
}
int main() {
int a[] = { 10, 15, 20 };
std::array<int, 3> b { 10, 10, 10 };
std::vector<int> v { 10, 20, 30 };
std::string s { "Hello, Stack Overflow" };
check(a, 10);
check(b, 15);
check(v, 20);
check(s, 'Z');
return 0;
}
Output:
10 found
15 not found
20 found
Z not found
One wants this to be done tersely.
Nothing makes code more unreadable then spending 10 lines to achieve something elementary.
In C++ (and other languages) we have all and any which help us to achieve terseness in this case. I want to check whether a function parameter is valid, meaning equal to one of a number of values.
Naively and wrongly, I would first write
if (!any_of({ DNS_TYPE_A, DNS_TYPE_MX }, wtype) return false;
a second attempt could be
if (!any_of({ DNS_TYPE_A, DNS_TYPE_MX }, [&wtype](const int elem) { return elem == wtype; })) return false;
Less incorrect, but looses some terseness.
However, this is still not correct because C++ insists in this case (and many others) that I specify both start and end iterators and cannot use the whole container as a default for both. So, in the end:
const vector validvalues{ DNS_TYPE_A, DNS_TYPE_MX };
if (!any_of(validvalues.cbegin(), validvalues.cend(), [&wtype](const int elem) { return elem == wtype; })) return false;
which sort of defeats the terseness, but I don't know a better alternative...
Thank you for not pointing out that in the case of 2 values I could just have just if ( || ). The best approach here (if possible) is to use a case structure with a default where not only the values are checked, but also the appropriate actions are done.
The default case can be used for signalling an invalid value.
You can use old C-style programming to do the job. This will require little knowledge about C++. Good for beginners.
For modern C++ language you usually accomplish this through lambda, function objects, ... or algorithm: find, find_if, any_of, for_each, or the new for (auto& v : container) { } syntax. find class algorithm takes more lines of code. You may also write you own template find function for your particular need.
Here is my sample code
#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;
/**
* This is old C-like style. It is mostly gong from
* modern C++ programming. You can still use this
* since you need to know very little about C++.
* #param storeSize you have to know the size of store
* How many elements are in the array.
* #return the index of the element in the array,
* if not found return -1
*/
int in_array(const int store[], const int storeSize, const int query) {
for (size_t i=0; i<storeSize; ++i) {
if (store[i] == query) {
return i;
}
}
return -1;
}
void testfind() {
int iarr[] = { 3, 6, 8, 33, 77, 63, 7, 11 };
// for beginners, it is good to practice a looping method
int query = 7;
if (in_array(iarr, 8, query) != -1) {
cout << query << " is in the array\n";
}
// using vector or list, ... any container in C++
vector<int> vecint{ 3, 6, 8, 33, 77, 63, 7, 11 };
auto it=find(vecint.begin(), vecint.end(), query);
cout << "using find()\n";
if (it != vecint.end()) {
cout << "found " << query << " in the container\n";
}
else {
cout << "your query: " << query << " is not inside the container\n";
}
using namespace std::placeholders;
// here the query variable is bound to the `equal_to` function
// object (defined in std)
cout << "using any_of\n";
if (any_of(vecint.begin(), vecint.end(), bind(equal_to<int>(), _1, query))) {
cout << "found " << query << " in the container\n";
}
else {
cout << "your query: " << query << " is not inside the container\n";
}
// using lambda, here I am capturing the query variable
// into the lambda function
cout << "using any_of with lambda:\n";
if (any_of(vecint.begin(), vecint.end(),
[query](int val)->bool{ return val==query; })) {
cout << "found " << query << " in the container\n";
}
else {
cout << "your query: " << query << " is not inside the container\n";
}
}
int main(int argc, char* argv[]) {
testfind();
return 0;
}
Say this file is named 'testalgorithm.cpp'
you need to compile it with
g++ -std=c++11 -o testalgorithm testalgorithm.cpp
Hope this will help. Please update or add if I have made any mistake.
If you were originally looking for the answer to this question (int value in sorted (Ascending) int array), then you can use the following code that performs a binary search (fastest result):
static inline bool exists(int ints[], int size, int k) // array, array's size, searched value
{
if (size <= 0) // check that array size is not null or negative
return false;
// sort(ints, ints + size); // uncomment this line if array wasn't previously sorted
return (std::binary_search(ints, ints + size, k));
}
edit: Also works for unsorted int array if uncommenting sort.
You can do it in a beginners style by using control statements and loops..
#include <iostream>
using namespace std;
int main(){
int arr[] = {10,20,30,40,50}, toFind= 10, notFound = -1;
for(int i = 0; i<=sizeof(arr); i++){
if(arr[i] == toFind){
cout<< "Element is found at " <<i <<" index" <<endl;
return 0;
}
}
cout<<notFound<<endl;
}
C++ has NULL as well, often the same as 0 (pointer to address 0x00000000).
Do you use NULL or 0 (zero) for pointers in C++?
So in C++ that null check would be:
if (!foo)
cout << "not found";