www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Issue with char and string overlap

reply "JS" <js.mdnq gmail.com> writes:
I'm trying to create a split function that can handle both char 
and string delims. I initially created two separate functions but 
this doesn't work for default parameters since the compiler 
doesn't know which one to choose(but in this case both would work 
fine and it would be nice to inform the compiler of that). I then 
tried to template and conditionally code the two but it still 
doesn't work:

both functions work separately but when i uncomment the string 
version I get an error about the string version shadowing.

import std.stdio, std.cstream;

string[] split(T)(string s, T d) if (is(T == char) || is(T == 
string))
{
	int i = 0, oldj = 0; bool ok = true;
	string[] r;
	foreach(j, c; s)
	{
		static if (is(T == char))
		{
			if (c == d)
			{
				if (!ok) { oldj++; continue; }
				if (r.length <= i) r.length += 5;
				r[i] = s[oldj..j];
				i++; oldj = j+1;
				ok = false;
			} else if (!ok) ok = true;
		}
		else if (is(T == string))
		{
		/*
			for(int j = 0; j < s.length - d.length; j++)
			{
				if (s[j..j + d.length] == d)
				{
					if (!ok) { oldj++; continue; }
					if (i == r.length) r.length += 5;
					r[i] = s[oldj..j - d.length + 1];
					i++; oldj = j + d.length;
					ok = false;
				} else if (!ok) ok = true;
		
			}
		*/
		}
	}
	if (oldj < s.length)
	{
		if (r.length <= i) r.length++;
		r[i] = s[oldj..$];
		i++;
	}
	r.length = i;
	return r;
}


string[] splitS(string s, string d = " ")
{
	int i = 0, oldj = 0; bool ok = true;
	string[] r;
	for(int j = 0; j < s.length - d.length; j++)
	{
		if (s[j..j + d.length] == d)
		{
			if (!ok) { oldj++; continue; }
                         if (r.length <= i) r.length += 5;
			r[i] = s[oldj..j - d.length + 1];
			i++; oldj = j + d.length;
			ok = false;
		} else if (!ok) ok = true;
		
	}
	if (oldj < s.length)
	{
		if (r.length <= i) r.length++;
		r[i] = s[oldj..$];
		i++;
	}

	r.length = i;
	return r;
}

void main(string[] args)
{

	auto s = splitS("abc bas   ccc", " ");
	
	foreach(a; s) writeln(a);
	
	din.getc();
}
Jul 19 2013
next sibling parent reply "JS" <js.mdnq gmail.com> writes:
On Friday, 19 July 2013 at 17:18:00 UTC, JS wrote:
 I'm trying to create a split function that can handle both char 
 and string delims. I initially created two separate functions 
 but this doesn't work for default parameters since the compiler 
 doesn't know which one to choose(but in this case both would 
 work fine and it would be nice to inform the compiler of that). 
 I then tried to template and conditionally code the two but it 
 still doesn't work:

 both functions work separately but when i uncomment the string 
 version I get an error about the string version shadowing.

 import std.stdio, std.cstream;

 string[] split(T)(string s, T d) if (is(T == char) || is(T == 
 string))
 {
 	int i = 0, oldj = 0; bool ok = true;
 	string[] r;
 	foreach(j, c; s)
 	{
 		static if (is(T == char))
 		{
 			if (c == d)
 			{
 				if (!ok) { oldj++; continue; }
 				if (r.length <= i) r.length += 5;
 				r[i] = s[oldj..j];
 				i++; oldj = j+1;
 				ok = false;
 			} else if (!ok) ok = true;
 		}
 		else if (is(T == string))
 		{
 		/*
 			for(int j = 0; j < s.length - d.length; j++)
 			{
 				if (s[j..j + d.length] == d)
 				{
 					if (!ok) { oldj++; continue; }
 					if (i == r.length) r.length += 5;
 					r[i] = s[oldj..j - d.length + 1];
 					i++; oldj = j + d.length;
 					ok = false;
 				} else if (!ok) ok = true;
 		
 			}
 		*/
 		}
 	}
 	if (oldj < s.length)
 	{
 		if (r.length <= i) r.length++;
 		r[i] = s[oldj..$];
 		i++;
 	}
 	r.length = i;
 	return r;
 }


 string[] splitS(string s, string d = " ")
 {
 	int i = 0, oldj = 0; bool ok = true;
 	string[] r;
 	for(int j = 0; j < s.length - d.length; j++)
 	{
 		if (s[j..j + d.length] == d)
 		{
 			if (!ok) { oldj++; continue; }
                         if (r.length <= i) r.length += 5;
 			r[i] = s[oldj..j - d.length + 1];
 			i++; oldj = j + d.length;
 			ok = false;
 		} else if (!ok) ok = true;
 		
 	}
 	if (oldj < s.length)
 	{
 		if (r.length <= i) r.length++;
 		r[i] = s[oldj..$];
 		i++;
 	}

 	r.length = i;
 	return r;
 }

 void main(string[] args)
 {

 	auto s = splitS("abc bas   ccc", " ");
 	
 	foreach(a; s) writeln(a);
 	
 	din.getc();
 }
BTW, I'd like to have a default value for d. That or efficiently allow for variadic d, which then the default delim could easily be tested for.
Jul 19 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 19 July 2013 at 17:25:34 UTC, JS wrote:
 BTW, I'd like to have a default value for d. That or efficiently
 allow for variadic d, which then the default delim could easily
 be tested for.
To answer your previous question about shadowing, you are probably experiencing an old bug where you can't overload a template and non-template. This was fixed in HEAD, but is not yet available in a packaged version (eg, it is not in 2.063.2). The standard workaround is declaring your function as a "parameter-less parameterized function (!)" string[] split()(string s, string d); //This is actually a template. If you want d to be variadic, while still having a default case, any number of solutions are available, including simply doing this: string[] split()(string s); (1*) string[] split(Args...)(string s, Args args); (2) (1) is "more specialized" (I think), so will be considered the better match for "split("hello")". IF I'm wrong, simply add "if (Args.length > 0)" as a template restriction for the second function, and you are good to go. If you have access to head, then declare (1) as a straight up function. In that case, it most certainly *will* be the better match.
Jul 19 2013
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 19 July 2013 at 17:18:00 UTC, JS wrote:
 I'm trying to create a split function that can handle both char 
 and string delims. I initially created two separate functions 
 but this doesn't work for default parameters since the compiler 
 doesn't know which one to choose(but in this case both would 
 work fine and it would be nice to inform the compiler of that).
Simply provide a default parameter for *one* of the functions. EG: string[] split(string s, char d = ' '); string[] split(string s, string d); This lifts the ambiguity. If *both* have default params, then the ambiguity is simply not solvable, even by a human being. Another solution is the "no default multiple sig" option, eg: string[] split(string s) { return split(s, ' '); } string[] split(string s, char d); string[] split(string s, string d); PS: Are you doing this to learn? std.array.split does the same thing for you.
 I then tried to template and conditionally code the two but it 
 still doesn't work:

 both functions work separately but when i uncomment the string 
 version I get an error about the string version shadowing.

 import std.stdio, std.cstream;

 string[] split(T)(string s, T d) if (is(T == char) || is(T == 
 string))
 {
 	int i = 0, oldj = 0; bool ok = true;
 	string[] r;
 	foreach(j, c; s)
 	{
 		static if (is(T == char))
 		{
 			if (c == d)
 			{
 				if (!ok) { oldj++; continue; }
 				if (r.length <= i) r.length += 5;
 				r[i] = s[oldj..j];
 				i++; oldj = j+1;
 				ok = false;
 			} else if (!ok) ok = true;
 		}
 		else if (is(T == string))
 		{
 		/*
 			for(int j = 0; j < s.length - d.length; j++)
 			{
 				if (s[j..j + d.length] == d)
 				{
 					if (!ok) { oldj++; continue; }
 					if (i == r.length) r.length += 5;
 					r[i] = s[oldj..j - d.length + 1];
 					i++; oldj = j + d.length;
 					ok = false;
 				} else if (!ok) ok = true;
 		
 			}
 		*/
 		}
 	}
 	if (oldj < s.length)
 	{
 		if (r.length <= i) r.length++;
 		r[i] = s[oldj..$];
 		i++;
 	}
 	r.length = i;
 	return r;
 }


 string[] splitS(string s, string d = " ")
 {
 	int i = 0, oldj = 0; bool ok = true;
 	string[] r;
 	for(int j = 0; j < s.length - d.length; j++)
 	{
 		if (s[j..j + d.length] == d)
 		{
 			if (!ok) { oldj++; continue; }
                         if (r.length <= i) r.length += 5;
 			r[i] = s[oldj..j - d.length + 1];
 			i++; oldj = j + d.length;
 			ok = false;
 		} else if (!ok) ok = true;
 		
 	}
 	if (oldj < s.length)
 	{
 		if (r.length <= i) r.length++;
 		r[i] = s[oldj..$];
 		i++;
 	}

 	r.length = i;
 	return r;
 }

 void main(string[] args)
 {

 	auto s = splitS("abc bas   ccc", " ");
 	
 	foreach(a; s) writeln(a);
 	
 	din.getc();
 }
Jul 19 2013
prev sibling next sibling parent reply "anonymous" <anonymous example.com> writes:
On Friday, 19 July 2013 at 17:18:00 UTC, JS wrote:
 both functions work separately but when i uncomment the string 
 version I get an error about the string version shadowing.

 import std.stdio, std.cstream;

 string[] split(T)(string s, T d) if (is(T == char) || is(T == 
 string))
 {
 	int i = 0, oldj = 0; bool ok = true;
i and oldj should probably be size_t. The same for other ints throughout.
 	string[] r;
 	foreach(j, c; s)
The first j is declared here.
 	{
 		static if (is(T == char))
 		{
 			if (c == d)
 			{
 				if (!ok) { oldj++; continue; }
 				if (r.length <= i) r.length += 5;
 				r[i] = s[oldj..j];
 				i++; oldj = j+1;
 				ok = false;
 			} else if (!ok) ok = true;
 		}
 		else if (is(T == string))
 		{
 		/*
 			for(int j = 0; j < s.length - d.length; j++)
This j would shadow the one above. Just choose another name.
 			{
 				if (s[j..j + d.length] == d)
 				{
 					if (!ok) { oldj++; continue; }
 					if (i == r.length) r.length += 5;
 					r[i] = s[oldj..j - d.length + 1];
 					i++; oldj = j + d.length;
 					ok = false;
 				} else if (!ok) ok = true;
 		
 			}
 		*/
 		}
 	}
 	if (oldj < s.length)
 	{
 		if (r.length <= i) r.length++;
 		r[i] = s[oldj..$];
 		i++;
 	}
 	r.length = i;
 	return r;
 }
Jul 19 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/19/2013 10:40 AM, anonymous wrote:

 On Friday, 19 July 2013 at 17:18:00 UTC, JS wrote:
             for(int j = 0; j < s.length - d.length; j++)
This j would shadow the one above. Just choose another name.
Even better: foreach (k; 0 .. s.length - d.length) or: foreach (k; iota(s.length - d.length)) Also, either code must deal with the case where d.length is greater than s.length. Otherwise, being a size_t, the subtraction will be a large value. Ali
Jul 19 2013
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jul 19, 2013 at 07:17:57PM +0200, JS wrote:
[...]
 string[] split(T)(string s, T d) if (is(T == char) || is(T ==
 string))
 {
 	int i = 0, oldj = 0; bool ok = true;
 	string[] r;
 	foreach(j, c; s)
 	{
 		static if (is(T == char))
 		{
 			if (c == d)
 			{
 				if (!ok) { oldj++; continue; }
 				if (r.length <= i) r.length += 5;
 				r[i] = s[oldj..j];
 				i++; oldj = j+1;
 				ok = false;
 			} else if (!ok) ok = true;
 		}
 		else if (is(T == string))
^^ This should be "else static if", otherwise it will probably not do what you expect. :) T -- There are two ways to write error-free programs; only the third one works.
Jul 19 2013