1

I have a few questions related to portions of my code.

The first has to do with how I find the length of an array of arrays of strings. I'm using the following as a map for a Calculus tool I'm using.

std::string dMap[][10] = {{"x", "1"}, {"log(x)", "1/x"}, {"e^x", "e^x"}};

I'm wondering how to do the equivalent of

int arr[] = {1, 69, 2};
int arrlen = sizeof(arr)/sizeof(int);

with an array of elements of type std::string. Also, is there a better way of storing symbolic representations of (f(x), f'(x)) pairs? I'm trying to not use C++11.

My next question has to do with a procedure I wrote that isn't working. Here it is:

std::string CalculusWizard::composeFunction(const std::string & fx, const char & x, const std::string & gx)
{
    /* Return fx compose gx, i.e. return a string that is gx with every instance of the character x replaced 
       by the equation gx. 
       E.g. fx="x^2", x="x", gx="sin(x)" ---> composeFunction(fx, x, gx) = "(sin(x))^2"
    */
    std::string hx(""); // equation to return 

    std::string lastString("");
    for (std::string::const_iterator it(fx.begin()), offend(fx.end()); it != offend; ++it)
    {
        if (*it == x)
        {
            hx += "(" + gx + ")";
            lastString.erase(lastString.begin(), lastString.end());
        }
        else
        {
            lastString.push_back(*it);
        }
    }

    return hx;
}

First of all, where's the bug in the procedure? It's not working when I test it out.

Second of all, when trying to make a string empty again, is it faster to do

lastString.erase(lastString.begin(), lastString.end());

or

lastString = ""; 

???

Thank you for your time.

  • 2
    Why not use vector of pairs, it will be much easy to code – coder hacker May 13 '14 at 03:00
  • I absolutely have to agree with @TejasPatel! `std::string dMap[][10]` is a pretty strange and unusual construct for c++ code. Probably you should consider to use `std::vector>` or some other more appropriate data structure for this instead, to avoid any troubles! – πάντα ῥεῖ May 13 '14 at 03:07
  • 2
    *I'm trying to not use C++11* - why? Do you like making life harder than it needs to be? – Praetorian May 13 '14 at 03:11
  • For the last question: clear a string using `lastString.clear()` - see http://en.cppreference.com/w/cpp/string/basic_string/clear – harmic May 13 '14 at 05:52

4 Answers4

1

Question 1) Understand that you can't, and really don't need to, calculate the size of a String this way. Just ask it how big it is and it will tell you.

// comparing size, length, capacity and max_size
#include <iostream>
#include <string>

int main ()
{
  std::string str ("Test string");
  std::cout << "size: " << str.size() << "\n";
  std::cout << "length: " << str.length() << "\n";
  std::cout << "capacity: " << str.capacity() << "\n";
  std::cout << "max_size: " << str.max_size() << "\n";
  return 0;
}

http://www.cplusplus.com/reference/string/string/capacity/

As for an array of strings, well go read this: How to determine the size of an array of strings in C++?

Check out David Rodríguez's answer.

Question 2) The better way might be to define a FunctionPair class depending on what you're doing with them. Vector<FunctionPair> might come in handy.

If FunctionPair doesn't end up with any behavior (functions) associated with it then a struct might be enough: std::pair<std::string, std::string> could also be shoved into a vector.

You don't need a map unless your going to use one function string to look up the other. http://www.cplusplus.com/reference/map/map/

Question 3) A little better description of what's not working would help. I notice lastString doesn't impact hx at all.

Question 4) "Second of all" Fastest is nothing to worry about at this point. Write what is easiest to look at until all the bugs are gone. "Premature optimization is the root of all evil", Donald Knuth.

Tip: Look into how the replace function might help you do the composition replacements: http://www.cplusplus.com/reference/string/string/replace/

Community
  • 1
  • 1
candied_orange
  • 7,036
  • 2
  • 28
  • 62
1

As the above commenter said, you shouldn't use c-style arrays even if you just want to make things 'easy'.

In reality doing things like that makes things harder.

c-style arrays aren't bounds checked. That means they are a source of bugs due to memory unsafety and can lead to all kinds of issues from segfaulting to corrupting data as you read random data from unrelated blocks of memory or even worse write to them.

#include <iostream>

int main() {
  int nums[] = {1, 2, 3};
  std::cout << nums[3] << std::endl;
}

.

# ./a.out 
4196544

No programmer is perfect, every time you implement something like that there is a percentage chance you will be off by one in your bounds or something. Even if you are some programming god most people have to work on a team with people who aren't. In many cases no one will even notice since not every time will cause anything obvious. Memory can be randomly corrupted without causing anything to crash horribly. Until you make a totally unrelated change that causes the memory to be in a different order.

But when you do notice it will often effect something totally unrelated that you code sometime later. Given the fact that you will likely implement many such arrays in your programming lifetime you will likely make things much worse for yourself, you save yourself 10 minutes for each project but end up spending hours tracking down a bug in one.

If you really don't want C++11 then use std::vector<std::vector<std::string>>. It will use a little more memory so you might loose some performance , but most of the time when people are worried about performance they shouldn't be. Are you are calling this function 10,000 time a second? Even then you could gain more performance from threading the code or preallocating memory. Most of the time people think something has bad performance but in reality the computer is optimizing it away, or the CPU is. Is the performance from the memory allocation going to be worse than trying to find the array size every run?

This is also the case with raw pointers vs std::unique_ptr, std::shared_ptr.

If typing all those names looks like a pain, use a typedef to make it nice.

You can also look at using Boost's Array type, boost::array. Or whip up your own custom class.

That's not to say that you should never use that stuff. But you should only use it when you can justify it. The default should be the 'pure' C++ style code.

  • Performance (only when you have measured and see that you need it there).
  • C compatibility (but most of the time you can just wrap that stuff in the std classes anyway).

If you do feel you need it then. Make sure you unittest your code. And look at using the address and memory sanitizers that ship in current versions of gcc and clang. And quarantine the code as much as possible (ie in classe)s.

That all sounds like a lot of work, but once you have learned to do it, it becomes a habit and build it into your build system then it's just part of the development process. As easy as make test. And once you have it in one build system, you cut and paste it into everything else you do forever. You have expanded your programmers toolkit. That's all good habits to form even if you don't do that.

But here's the actual answer to your array size question:

  std::string arr[][10] = {
    {"xxx", "111"},
    {"y", "222"},
    {"hello", "goodbye"},
    {"I like candy", "mmmm"},
    {"Math goes here", "this is math"},
    {"More random stuff", "adsfdsfasf"},
  };
  int size = sizeof(arr) / 10 / sizeof(std::string);
  std::cout << size << endl; // Prints 6, as in 6 pairs of strings
David C. Bishop
  • 6,437
  • 3
  • 28
  • 22
0

Since the semantics is similar as Map ( you are mapping a function to it's differential), I guess most suitable data structure would be std::map, when you can easily get the differential using the function as index.

About the function, you are not appending lastString.

return hx+lastString;
Rakib
  • 7,435
  • 7
  • 29
  • 45
0

Question 1 is actually quite straightforward:

std::string dMap[][10] = {{"x", "1"}, {"log(x)", "1/x"}, {"e^x", "e^x"}};
size_t tupleCount = sizeof(dMap)/sizeof(dMap[0]);
size_t maxTupleSize = sizeof(dMap[0])/sizeof(dMap[0][0]);

assert(tupleCount == 3);
assert(maxTupleSize == 10);

Note that you won't get the actual count of strings in a tuple this way. You only get the amount of std::strings that can fit into each tuple. Of course, you can search your tuples for the first default constructed std::string it contains. But the entire setup is an invitation for bugs, so you don't want to use it anyways (see below).


Question 2 can also be answered quite clearly. You should be using an std::unordered_map<>. Why?

  1. You usecase is to map strings of one class to another. That is the semantics of either std::map<> or std::unordered_map<>.

  2. From your question I gather that you don't need a notion of a next or previous mapping, your mapping pairs are essentially unrelated. In this case, std::unordered_map<> is simply faster than std::map<> because it uses a hash table internally. No matter how big your std::unordered_map<> gets, looking up its elements takes a constant amount of time. This is not true for std::map<>.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106