0

I answered a question here: https://stackoverflow.com/a/28862668/2642059 Where I needed to use recurrence to step through a string. I wanted to use a const string& as my parameter on each function, but unless I wanted to reconstruct the string each recursion I found that I needed to pass a start and finish position as well as the string itself. So it became pointless to pass the string at all.

In the end I choose to just pass a start and finish pointer to the char[].


As an example, say that I'm given a string which contains nested parenthesis (but no side by side parenthetical insertions.) So like this:

(abc(def(ghi((j)klm)nop)qrs)tuv)wxyz

But not like this:

(abc(def)(ghi)(j)(klm)(nop)(qrs)tuv)wxyz

I want to write a recursive program to extract the string in the deepest nested parentheses. Something like:

string foo(const string& bar){
    auto start = bar.find('(') + 1;

    return start == string::npos + 1 ? bar : foo(bar.substr(start, bar.find_last_of(')') - start));
}

However I'm unhappy reconstructing a string for each recurrence of foo. The alternative is to pass start and finish pointers as in the linked example (or to pass string::const_iterators.)

Is there a wrapper or something which would allow me to use string functionality, but not reconstruct a string?

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 1
    `std::experimantal::string_view` would probably help. With some time travel you can even get it in `std` probably! You could fake it, esp if you only need a subset of string functionality. – Yakk - Adam Nevraumont Mar 12 '15 at 10:58
  • @Yakk I don't see that listed here: http://en.cppreference.com/w/cpp/experimental Do you happen to know what TS that's part of? – Jonathan Mee Mar 12 '15 at 11:02
  • I know it's just an example, but you don't need recursion to find the deepest nested parentheses. you can just search for the last '(' and the first ')' :) – samgak Mar 12 '15 at 11:02
  • @samgak I thought for a while trying to come up with a good way to showcase recursion on a string but couldn't come up with anything better. If you have something that would make a better example, please let me know. Obviously from my linked answer I wouldn't do recursion to find this, but rather I'd do this: http://stackoverflow.com/q/28863236/2642059 – Jonathan Mee Mar 12 '15 at 11:07
  • 1
    [library fundamentals TS?](http://en.cppreference.com/w/cpp/header/experimental/string_view) – Yakk - Adam Nevraumont Mar 12 '15 at 11:49

2 Answers2

3

string_view from the library fundamentals TS might be one idea, support is available in GCC.

The interface is virtually identical to string

#include <experimental/string_view>
using std::experimental::string_view;

string_view foo(const string_view& bar){
    auto start = bar.find('(') + 1;

    return start == string_view::npos + 1 ? bar : foo(bar.substr(start, bar.find_last_of(')') - start));
}

The last line could also be

return start ? foo(bar.substr(start, bar.find_last_of(')') - start)) : bar;

Although they're both pretty cryptic.

user657267
  • 20,568
  • 5
  • 58
  • 77
  • I have to do `string::npos + 1` cause I did this above: `auto start = bar.find('(') + 1;` So if the `'('` wasn't found `start` will be: `string::npos + 1`. – Jonathan Mee Mar 12 '15 at 11:10
  • @JonathanMee I see, it kind of threw me off because using `npos` in arithmetic isn't something you see everyday. – user657267 Mar 12 '15 at 11:20
  • `auto start = bar.find('('); if(start==string_view::npos)return bar;++start;auto mid = bar.substr(start, bar.find_last_of(')')-start); return foo(mid); }` is more sane, no? – Yakk - Adam Nevraumont Mar 12 '15 at 11:55
  • @user657267 Yeah, [as mentioned by samgak](http://stackoverflow.com/questions/29007753/pass-a-string-recursively-without-recreation?noredirect=1#comment46261315_29007753) this isn't a great example, I just couldn't think of anything simpler to showcase my question. – Jonathan Mee Mar 12 '15 at 11:55
  • @user657267 I don't see support for this yet in Visual Studio 2015. I guess `const char*`s or `string::const_iterator`s are my options till the Fundamentals TS is accepted. (Fingers crossed for C++1z.) – Jonathan Mee Mar 12 '15 at 12:02
  • I wish that I could accept both answers. When this is incorporated into the standard it will clearly be the go-to solution. It's really [Yakk's solution](http://stackoverflow.com/a/29009821/2642059) that should be used until we have access to `string_view` though. – Jonathan Mee Mar 13 '15 at 10:52
1

Write your own array_view<T>. It is a few dozen lines of code.

Use std::find to replace both algorithms. In one case, use reverse iterators. (or write a range-based find and range-based backwards)

Use {T*,T*} ctor to recurse.

array_view<const char> foo(array_view<const char> bar)

Here is a primitive array_view<T>:

template<class T>
struct array_view {
  using mutable_T = typename std::remove_reference<T>::type;
  // 3 primitive functions:
  T* begin() const { return b; }
  T* end() const { return e; }
  array_view(T* s, T* f):b(s), e(f) {};
  // everything else based on them:
  size_t size() const { return end()-begin(); }
  array_view(T* s, size_t l):array_view(s,s+l) {}
  array_view():array_view(nullptr,  nullptr) {}
  // repeat next 3 for string, array, initializer list, C array as required:
  template<class A>
  array_view( std::vector<T,A>& v ):array_view(v.data(), v.size()) {}
  // may not compile for non-const T, but that is ok  you get an error:
  template<class A>
  array_view( std::vector<mutable_T,A>const & v ):array_view(v.data(), v.size()) {}
  // in a better world, you'd SFINAE remove the above from consideration
  // consider it for your next iteration of array_view.
  // conversion to containers:
  template<class A>
  explicit operator std::vector<mutable_T,A>() const {
    return convert_to< std::vector<mutable_T, A> >();
  }
  template<class C>
  C convert_to() const {
    C retval(begin(), end());
    return retval;
  }

  // utility functions:
  T& front() const { return *begin(); }
  T& back() const { return std::prev(*end()); }
  // Also rbegin, rend, and whatever else you find you are missing

  // inspired by std::experimental:
  void pop_front() { *this = {std::next(begin()), end()}; }
  void pop_back() { *this = {begin(), std::prev(end())}; }
  // but I find direct use of `view = {x,y}` almost as good
  // these kind of operations are the ONLY ones that are non-const
  // remember this is a view.  If you want a view of const data, make it
  // an array_view<const T>, not a const array_view<T>.  It really works
  // out better.
private:
  T* b
  T* e;
};

the above sample code is not tested.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • I'm not sure if I understand your last statement: "Use `{T*,T*}` ctor to recurse." Isn't that going to construct a `string`, which this question was trying to do away with? – Jonathan Mee Mar 12 '15 at 12:33
  • @jona no. An `array_view` never owns, never copies. It is a range view on `T*` with implicit ctors from vector, string, array, C arrays, and initializer_list, plus `T*,size_t` and `T*,T*`. All it stores is two pointers. It exposes range concepts: begin, end, data, size, empty, front, back, etc. (all `const`). It is surprisingly useful and easy to write a usable one (if imperfect). Have your code take and return array view on char. – Yakk - Adam Nevraumont Mar 12 '15 at 13:00
  • Duh, thank you it makes more sense now. Yet, as you mention in your comment, I think `string_view` would be the first choice as soon as it's available, as this is effectively passing two pointers just as in my linked answer. – Jonathan Mee Mar 12 '15 at 13:10
  • 1
    @JonathanMee `string_view` is also just two pointers. All of the view dross is just to no longer have to manually mess with those pointers as directly, and to be able to view them as a single chunk of information. I find once I had an `array_view` in my code base, half of the functions that used to take `vector` now took `array_view`, and it enabled really easy legacy code rewrites (with zero performance hit) to be container-based. Give it a stab. There are `std::experimental` stuff coming from Eric that is similar to the above (range views), I don't know if he has a contig range view. – Yakk - Adam Nevraumont Mar 12 '15 at 13:26