digitalmars.D - TDPL: Overloading template functions
- Andrej Mitrovic (68/68) Jul 27 2010 There are two templated functions that overload against each other, and ...
- Philippe Sigaud (24/30) Jul 28 2010 On Wed, Jul 28, 2010 at 05:50, Andrej Mitrovic
- Ryan W Sims (14/44) Aug 01 2010 If I understand the 'is' statement correctly, wouldn't this be a little
- Andrej Mitrovic (4/37) Jul 28 2010 These templates seem to be hard to get right. I guess with experience th...
- Andrei Alexandrescu (3/4) Jul 28 2010 Is the explanation on page 141 adequate?
- Andrej Mitrovic (17/23) Jul 28 2010 It was adequate for the first function, but I never knew why you've chan...
- Jonathan M Davis (7/9) Jul 28 2010 Dynamic arrays are reference types (though static arrays are indeed valu...
- Andrej Mitrovic (4/15) Jul 28 2010 Woops, I got confused. I was thinking about structs, not arrays.
- Rainer Schuetze (14/18) Jul 28 2010 2.4 has no exact representation as a floating point value, as it is
- Andrej Mitrovic (9/29) Jul 28 2010 Should I just keep posting my findings in this thread? I hate to make a ...
- Andrej Mitrovic (36/74) Jul 28 2010 I can't get the example on page 158 to compile, I've tried both versions...
- Andrej Mitrovic (18/101) Jul 29 2010 Page 160, code:
- Andrej Mitrovic (33/150) Jul 29 2010 How do I print out an expanded tuple but with spaces between the values?
- Philippe Sigaud (42/74) Jul 30 2010 You can iterate on the values and create the corresponding string. Note ...
- Andrej Mitrovic (6/94) Jul 30 2010 Thanks Phillippe, that explains a lot.
- Andrej Mitrovic (26/114) Jul 30 2010 Hey, just tried out your code.
- Andrej Mitrovic (3/139) Jul 30 2010 I've found that the syntax mytuple.field[] can be used, I think this is
- Andrej Mitrovic (11/170) Jul 30 2010 Speaking of which, I think there's a typo in Phobos docs:
- Brad Roberts (2/15) Jul 30 2010 Fix submitted to svn.
- Philippe Sigaud (19/44) Jul 30 2010 Ah yes, that was typed over the course of 2-3 hours, with my children
- Andrej Mitrovic (39/58) Jul 28 2010 On page 143 there's a new find() overloaded function which is more
- Andrej Mitrovic (29/93) Jul 28 2010 Page 149, the two shorter-hand ways of writing function literals are not
- Andrej Mitrovic (5/117) Jul 28 2010 They do work if I specify the type before x, as in:
- Andrej Mitrovic (41/91) Jul 30 2010 Okay take a look at this:
- Philippe Sigaud (68/68) Jul 30 2010 First, trying a copy-paste of your code gives me an error in object.di
- Andrej Mitrovic (48/116) Jul 30 2010 So, maybe you just found a bug in object.di._ArrayEq. I'll post on the m...
- Andrej Mitrovic (81/150) Jul 31 2010 I think there really is some bug here. I tried to use your template help...
- Philippe Sigaud (21/40) Aug 01 2010 That's because the first part of the constraint is tested, to allow for
- Philippe Sigaud (45/54) Aug 01 2010 The assert fails, because find(d1, d2) does not returns [1.5, 2.4, 3] in...
- Andrej Mitrovic (8/32) Aug 01 2010 Let me see if I'm getting this right.
- Philippe Sigaud (8/22) Aug 01 2010 I was answering you about the "incompatible types" error. The first part...
There are two templated functions that overload against each other, and should pass the unittests. But there are a few problems. Here's the example code: T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool)) { while (haystack.length > 0 && haystack[0] != needle) { haystack = haystack[1 .. $]; } return haystack; } T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) { while (longer.length >= shorter.length) { if (longer[0 .. shorter.length] == shorter) break; longer = longer[1 .. $]; } return longer; } unittest { double[] d1 = [6.0, 1.5, 2.4, 3]; float[] d2 = [1.5, 2.4]; assert(find(d1, d2) == d1[1 .. $]); } unittest { double[] d = [1.5, 2.4]; assert(find(d, 1.0) == null); assert(find(d, 1.5) == d); string[] s = ["one", "two"]; assert(find(s, "two") == ["two"]); } The first unittest will fail. There is some funky issue going around here. This small code will pass the unittest: import std.stdio; void main() { } float x = 4.0; double y = 4.0; unittest { assert(x == y); } But this one will not: import std.stdio; void main() { } float x = 4.2; double y = 4.2; unittest { assert(x == y); } Notice I've only changed the 0's to a different number. So, if I change the first unittest in the original code to this: unittest { double[] d1 = [6.0, 1.0, 2.0, 3]; float[] d2 = [1.0, 2.0]; assert(find(d1, d2) == d1[1 .. $]); } It will pass. I guess this has something to do with internal representation of floats/doubles.. The second unittest from the Example code, which I'm pasting here again for convenience: unittest { double[] d = [1.5, 2.4]; assert(find(d, 1.0) == null); assert(find(d, 1.5) == d); string[] s = ["one", "two"]; assert(find(s, "two") == ["two"]); } Gives a compiler error saying that both of the overloaded templated functions match for the call (the last assert in the unittest). Also, Andrei, you never explained the if(is()) signature of the second templated function. I hope to get some pointers on that. :)
Jul 27 2010
On Wed, Jul 28, 2010 at 05:50, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote: I won't comment on the double/float issue, I do not anything about it.T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool)) T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) Also, Andrei, you never explained the if(is()) signature of the second templated function. I hope to get some pointers on that. :)I'll have a try at this one. As you may know, the is(typeof()) syntax is a way to try to compile an expression and see if it works. If it works, it has a type, given by typeof and is(Type) returns true. So the first one is really saying: "Hey compiler, may I compare an element of haystack (of type T) with an E In this case, longer is an array. So longer[0] would be an element, a T1. There is no chance that shorter will match as an element cannot be equal to an array ... except, of course, if longer is an array of T2[] (T1 == T2[]). Longer is a T2[][]. That may happen for arrays of strings, strings being arrays of chars. And I think that's the problem you have in your last unit test. Anyway, he takes longer[0..1] to get a slice, which is a dynamic array, a T1[] and hence comparable to a T2[]... most of the time. As for using ': bool' instead of '== bool' as in the first find(), I don't think there is any difference in this case. I understand T == U as 'T is the exact same type as U', whereas T : U is for me "T is a subtype of U". But then, a subtype of bool is pretty much constrained to be a bool. The T : U syntax is pretty much only used for classes : if(is(T : MyClass)) is saying : 'compile this only if T is a subclass of MyClass'. Philippe , except if longer is in fact
Jul 28 2010
On 7/28/10 7:44 AM, Philippe Sigaud wrote:On Wed, Jul 28, 2010 at 05:50, Andrej Mitrovic <andrej.mitrovich gmail.com <mailto:andrej.mitrovich gmail.com>> wrote: I won't comment on the double/float issue, I do not anything about it. T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool)) T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) Also, Andrei, you never explained the if(is()) signature of the second templated function. I hope to get some pointers on that. :)If I understand the 'is' statement correctly, wouldn't this be a little less verbose and still the same semantics? T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle)) { ...do the find... } (i.e. just drop the "== bool" from the guard?) When I compiled it, assert(find([1.0, 2.0], 2) == [2.0]) compiled fine, and assert(find([1, 2], "2") == []) didn't which is expected. Is there something I'm missing? -- rwsimsI'll have a try at this one. As you may know, the is(typeof()) syntax is a way to try to compile an expression and see if it works. If it works, it has a type, given by typeof and is(Type) returns true. So the first one is really saying: "Hey compiler, may I compare an element of haystack (of type T) with an E In this case, longer is an array. So longer[0] would be an element, a T1. There is no chance that shorter will match as an element cannot be equal to an array ... except, of course, if longer is an array of T2[] (T1 == T2[]). Longer is a T2[][]. That may happen for arrays of strings, strings being arrays of chars. And I think that's the problem you have in your last unit test. Anyway, he takes longer[0..1] to get a slice, which is a dynamic array, a T1[] and hence comparable to a T2[]... most of the time. As for using ': bool' instead of '== bool' as in the first find(), I don't think there is any difference in this case. I understand T == U as 'T is the exact same type as U', whereas T : U is for me "T is a subtype of U". But then, a subtype of bool is pretty much constrained to be a bool. The T : U syntax is pretty much only used for classes : if(is(T : MyClass)) is saying : 'compile this only if T is a subclass of MyClass'. Philippe , except if longer is in fact
Aug 01 2010
These templates seem to be hard to get right. I guess with experience they get easier to write and comprehend. Anyways, thanks for the explanation. On Wed, Jul 28, 2010 at 4:44 PM, Philippe Sigaud <philippe.sigaud gmail.com>wrote:On Wed, Jul 28, 2010 at 05:50, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:I won't comment on the double/float issue, I do not anything about it.T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool)) T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) Also, Andrei, you never explained the if(is()) signature of the second templated function. I hope to get some pointers on that. :)I'll have a try at this one. As you may know, the is(typeof()) syntax is a way to try to compile an expression and see if it works. If it works, it has a type, given by typeof and is(Type) returns true. So the first one is really saying: "Hey compiler, may I compare an element of haystack (of type T) with an E In this case, longer is an array. So longer[0] would be an element, a T1. There is no chance that shorter will match as an element cannot be equal to an array ... except, of course, if longer is an array of T2[] (T1 == T2[]). Longer is a T2[][]. That may happen for arrays of strings, strings being arrays of chars. And I think that's the problem you have in your last unit test. Anyway, he takes longer[0..1] to get a slice, which is a dynamic array, a T1[] and hence comparable to a T2[]... most of the time. As for using ': bool' instead of '== bool' as in the first find(), I don't think there is any difference in this case. I understand T == U as 'T is the exact same type as U', whereas T : U is for me "T is a subtype of U". But then, a subtype of bool is pretty much constrained to be a bool. The T : U syntax is pretty much only used for classes : if(is(T : MyClass)) is saying : 'compile this only if T is a subclass of MyClass'. Philippe , except if longer is in fact
Jul 28 2010
Andrej Mitrovic wrote:Also, Andrei, you never explained the if(is()) signature of the second templated function. I hope to get some pointers on that. :)Is the explanation on page 141 adequate? Andrei
Jul 28 2010
It was adequate for the first function, but I never knew why you've changed "== bool" to ": bool", and why you're using slices in the second templated function. Philippe explained it in more detail so I'm starting to get it. Otherwise, the only issue left is the float vs double comparisons. For this unittest: double[] d1 = [6.0, 1.5, 2.4, 3]; float[] d2 = [1.5, 2.4]; assert(find(d1, d2) == d1[1 .. $]); Inside the second template function I've added a writeln, and I can see the if statement evaluates "[1.5, 2.4] == [1.5, 2.4]" to false and will not break out of the loop. I don't know whether this is a DMD bug or if "float[] d2" should be changed to "double[] d2" in TDPL, since that runs fine. IIRC arrays are value types and as such they are compared bit-by-bit to each other, right? And since doubles and floats have different sizes, that unittest then fails. On Wed, Jul 28, 2010 at 6:19 PM, Andrei Alexandrescu < SeeWebsiteForEmail erdani.org> wrote:Andrej Mitrovic wrote:Also, Andrei, you never explained the if(is()) signature of the second templated function. I hope to get some pointers on that. :)Is the explanation on page 141 adequate? Andrei
Jul 28 2010
On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:IIRC arrays are value types and as such they are compared bit-by-bit to each other, right?Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
Woops, I got confused. I was thinking about structs, not arrays. But yeah, in this case a float gets compared to a double and it seems to evaluate to false. On Wed, Jul 28, 2010 at 7:26 PM, Jonathan M Davis <jmdavisprog gmail.com>wrote:On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:IIRC arrays are value types and as such they are compared bit-by-bit to each other, right?Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
Andrej Mitrovic wrote:Woops, I got confused. I was thinking about structs, not arrays. But yeah, in this case a float gets compared to a double and it seems to evaluate to false.2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 28 2010
Should I just keep posting my findings in this thread? I hate to make a new thread for every problem I find.. Anyway, on Page 150, this code fails: auto f = (int i) {}; assert(is(f == function)); I've checked via typeid(), and f is a delegate by default, not a function. I've tested it both in a function's scope and in module scope, with the same results. On Wed, Jul 28, 2010 at 10:56 PM, Rainer Schuetze <r.sagitario gmx.de>wrote:Andrej Mitrovic wrote:Woops, I got confused. I was thinking about structs, not arrays. But yeah, in this case a float gets compared to a double and it seems to evaluate to false.2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 28 2010
I can't get the example on page 158 to compile, I've tried both versions of reduce: import std.stdio, std.range; property bool empty(T)(T[] a) { return a.length == 0; } property ref T front(T)(T[] a) { return a[0]; } void popFront(T)(ref T[] a) { a = a[1 .. $]; } V reduce(alias fun, V, R)(V x, R range) if (isInputRange!R && is(typeof(x = fun(x, range.front)))) { for (; !range.empty; range.popFront()) { x = fun(x, range.front); } return x; } unittest { int[] r = [10, 14, 3, 5, 23]; // compute the sum of all elements int sum = reduce!((a, b) { return a + b; })(0, r); assert(sum == 55); // compute minimum int min = reduce!((a, b) { return a < b ? a : b; })(r[0], r); assert(min == 3); } void main() { } Errors: reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) does not match any function template declaration reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) cannot deduce template function from argument types !(__dgliteral1)(int,int[]) reduce.d(31): Error: template instance errors instantiating template If I comment out the template constraints (the if statements for either version of reduce), the example will compile and the unittests will pass. On Wed, Jul 28, 2010 at 11:15 PM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:Should I just keep posting my findings in this thread? I hate to make a new thread for every problem I find.. Anyway, on Page 150, this code fails: auto f = (int i) {}; assert(is(f == function)); I've checked via typeid(), and f is a delegate by default, not a function. I've tested it both in a function's scope and in module scope, with the same results. On Wed, Jul 28, 2010 at 10:56 PM, Rainer Schuetze <r.sagitario gmx.de>wrote:Andrej Mitrovic wrote:Woops, I got confused. I was thinking about structs, not arrays. But yeah, in this case a float gets compared to a double and it seems to evaluate to false.2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 28 2010
Page 160, code: import std.algorithm, std.range; void main() {} double average() { return 1.0; } double average(double) { return 2.0; } // computes average of a set of numbers, passable directly or via an array double average(double[] values...) { if (values.empty) { throw new Exception("Average of zero elements in undefined"); } return reduce!((a, b) { return a + b; })(0.0, values) / values.length; } unittest { average(); average(2.5); } The book states this is an ambiguous call, but I can succesfully compile. On Thu, Jul 29, 2010 at 4:16 AM, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:I can't get the example on page 158 to compile, I've tried both versions of reduce: import std.stdio, std.range; property bool empty(T)(T[] a) { return a.length == 0; } property ref T front(T)(T[] a) { return a[0]; } void popFront(T)(ref T[] a) { a = a[1 .. $]; } V reduce(alias fun, V, R)(V x, R range) if (isInputRange!R && is(typeof(x = fun(x, range.front)))) { for (; !range.empty; range.popFront()) { x = fun(x, range.front); } return x; } unittest { int[] r = [10, 14, 3, 5, 23]; // compute the sum of all elements int sum = reduce!((a, b) { return a + b; })(0, r); assert(sum == 55); // compute minimum int min = reduce!((a, b) { return a < b ? a : b; })(r[0], r); assert(min == 3); } void main() { } Errors: reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) does not match any function template declaration reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) cannot deduce template function from argument types !(__dgliteral1)(int,int[]) reduce.d(31): Error: template instance errors instantiating template If I comment out the template constraints (the if statements for either version of reduce), the example will compile and the unittests will pass. On Wed, Jul 28, 2010 at 11:15 PM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:Should I just keep posting my findings in this thread? I hate to make a new thread for every problem I find.. Anyway, on Page 150, this code fails: auto f = (int i) {}; assert(is(f == function)); I've checked via typeid(), and f is a delegate by default, not a function. I've tested it both in a function's scope and in module scope, with the same results. On Wed, Jul 28, 2010 at 10:56 PM, Rainer Schuetze <r.sagitario gmx.de>wrote:Andrej Mitrovic wrote:Woops, I got confused. I was thinking about structs, not arrays. But yeah, in this case a float gets compared to a double and it seems to evaluate to false.2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 29 2010
How do I print out an expanded tuple but with spaces between the values? There's this example on page 164: import std.typecons, std.stdio; void fun(T...)(T args) { // create a tuple to pack all arguments together gun(tuple(args)); } void gun(T)(T value) { // expand the tuple back writeln(value.expand); } void main() { fun(1); fun(1, 2.2); } This prints out: 1 12.2 But that's confusing, since the values are "glued" together when printed out. I really want to print out: 1 1 2.2 Any ideas? One other thing. I can use the .length property for value and parameter tuples, but only if I haven't built them myself with the call to std.typecons.tuple(). For example I can call writeln(T.length) in the fun() function (where the compiler automatically constructs a tuple), but not in the gun() function. So there seems to be compiler tuples and user-constructed tuples, which are not the same. It's a bit of a shame that there isn't a chapter on tuples except this brief mention (I don't see it listed in the contents page). I guess I'll take a look at the implementation. On Thu, Jul 29, 2010 at 5:08 PM, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:Page 160, code: import std.algorithm, std.range; void main() {} double average() { return 1.0; } double average(double) { return 2.0; } // computes average of a set of numbers, passable directly or via an array double average(double[] values...) { if (values.empty) { throw new Exception("Average of zero elements in undefined"); } return reduce!((a, b) { return a + b; })(0.0, values) / values.length; } unittest { average(); average(2.5); } The book states this is an ambiguous call, but I can succesfully compile. On Thu, Jul 29, 2010 at 4:16 AM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:I can't get the example on page 158 to compile, I've tried both versions of reduce: import std.stdio, std.range; property bool empty(T)(T[] a) { return a.length == 0; } property ref T front(T)(T[] a) { return a[0]; } void popFront(T)(ref T[] a) { a = a[1 .. $]; } V reduce(alias fun, V, R)(V x, R range) if (isInputRange!R && is(typeof(x = fun(x, range.front)))) { for (; !range.empty; range.popFront()) { x = fun(x, range.front); } return x; } unittest { int[] r = [10, 14, 3, 5, 23]; // compute the sum of all elements int sum = reduce!((a, b) { return a + b; })(0, r); assert(sum == 55); // compute minimum int min = reduce!((a, b) { return a < b ? a : b; })(r[0], r); assert(min == 3); } void main() { } Errors: reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) does not match any function template declaration reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) cannot deduce template function from argument types !(__dgliteral1)(int,int[]) reduce.d(31): Error: template instance errors instantiating template If I comment out the template constraints (the if statements for either version of reduce), the example will compile and the unittests will pass. On Wed, Jul 28, 2010 at 11:15 PM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:Should I just keep posting my findings in this thread? I hate to make a new thread for every problem I find.. Anyway, on Page 150, this code fails: auto f = (int i) {}; assert(is(f == function)); I've checked via typeid(), and f is a delegate by default, not a function. I've tested it both in a function's scope and in module scope, with the same results. On Wed, Jul 28, 2010 at 10:56 PM, Rainer Schuetze <r.sagitario gmx.de>wrote:Andrej Mitrovic wrote:Woops, I got confused. I was thinking about structs, not arrays. But yeah, in this case a float gets compared to a double and it seems to evaluate to false.2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 29 2010
On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:How do I print out an expanded tuple but with spaces between the values? There's this example on page 164: import std.typecons, std.stdio; void fun(T...)(T args) { // create a tuple to pack all arguments together gun(tuple(args)); } void gun(T)(T value) { // expand the tuple back writeln(value.expand); } void main() { fun(1); fun(1, 2.2); } This prints out: 1 12.2 But that's confusing, since the values are "glued" together when printed out. I really want to print out: 1 1 2.2 Any ideas?You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.One other thing. I can use the .length property for value and parameter tuples, but only if I haven't built them myself with the call to std.typecons.tuple(). For example I can call writeln(T.length) in the fun() function (where the compiler automatically constructs a tuple), but not in the gun() function. So there seems to be compiler tuples and user-constructed tuples, which are not the same.Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);It's a bit of a shame that there isn't a chapter on tuples except this brief mention (I don't see it listed in the contents page). I guess I'll take a look at the implementation.Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
Thanks Phillippe, that explains a lot. I like to try out all the examples, I never take any of it for granted. But hardly any book comes with 100% working examples, even the much praised CPL is loaded with syntax mistakes. With that said I have to admit I'm really enjoying reading TDPL so far. On Fri, Jul 30, 2010 at 12:11 PM, Philippe Sigaud <philippe.sigaud gmail.comwrote:On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:How do I print out an expanded tuple but with spaces between the values? There's this example on page 164: import std.typecons, std.stdio; void fun(T...)(T args) { // create a tuple to pack all arguments together gun(tuple(args)); } void gun(T)(T value) { // expand the tuple back writeln(value.expand); } void main() { fun(1); fun(1, 2.2); } This prints out: 1 12.2 But that's confusing, since the values are "glued" together when printed out. I really want to print out: 1 1 2.2 Any ideas?You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.One other thing. I can use the .length property for value and parameter tuples, but only if I haven't built them myself with the call to std.typecons.tuple(). For example I can call writeln(T.length) in the fun() function (where the compiler automatically constructs a tuple), but not in the gun() function. So there seems to be compiler tuples and user-constructed tuples, which are not the same.Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);It's a bit of a shame that there isn't a chapter on tuples except this brief mention (I don't see it listed in the contents page). I guess I'll take a look at the implementation.Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
Hey, just tried out your code. In the first one there's a little typo (used 'i' instead of index), but that's ok. ;p It seems like I don't need to use the to!() template. This code seems to work fine: void gun(T)(T value) { foreach(Type; value.expand) write(Type, " "); writeln(); } But regardless, at least I now know how to traverse through my own tuples, so thanks for that one. In your second example: Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14); I get an error way deep in the library itself (this could call for a better error message, methinks): C:\DMD\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(320): Error: tuple index 2 exceeds 2 I've reversed the order, put types before identifiers and this works fine then: Tuple!(int, "index", double, "sum") t = tuple(1, 3.14); I'm not sure what you mean by "t has .first and .second as members", I can't seem to access those. Maybe you meant t.index and t.sum as members? On Fri, Jul 30, 2010 at 12:11 PM, Philippe Sigaud <philippe.sigaud gmail.comwrote:On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:How do I print out an expanded tuple but with spaces between the values? There's this example on page 164: import std.typecons, std.stdio; void fun(T...)(T args) { // create a tuple to pack all arguments together gun(tuple(args)); } void gun(T)(T value) { // expand the tuple back writeln(value.expand); } void main() { fun(1); fun(1, 2.2); } This prints out: 1 12.2 But that's confusing, since the values are "glued" together when printed out. I really want to print out: 1 1 2.2 Any ideas?You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.One other thing. I can use the .length property for value and parameter tuples, but only if I haven't built them myself with the call to std.typecons.tuple(). For example I can call writeln(T.length) in the fun() function (where the compiler automatically constructs a tuple), but not in the gun() function. So there seems to be compiler tuples and user-constructed tuples, which are not the same.Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);It's a bit of a shame that there isn't a chapter on tuples except this brief mention (I don't see it listed in the contents page). I guess I'll take a look at the implementation.Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
I've found that the syntax mytuple.field[] can be used, I think this is probably what you meant by .first and .second. On Fri, Jul 30, 2010 at 8:20 PM, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:Hey, just tried out your code. In the first one there's a little typo (used 'i' instead of index), but that's ok. ;p It seems like I don't need to use the to!() template. This code seems to work fine: void gun(T)(T value) { foreach(Type; value.expand) write(Type, " "); writeln(); } But regardless, at least I now know how to traverse through my own tuples, so thanks for that one. In your second example: Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14); I get an error way deep in the library itself (this could call for a better error message, methinks): C:\DMD\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(320): Error: tuple index 2 exceeds 2 I've reversed the order, put types before identifiers and this works fine then: Tuple!(int, "index", double, "sum") t = tuple(1, 3.14); I'm not sure what you mean by "t has .first and .second as members", I can't seem to access those. Maybe you meant t.index and t.sum as members? On Fri, Jul 30, 2010 at 12:11 PM, Philippe Sigaud < philippe.sigaud gmail.com> wrote:On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:How do I print out an expanded tuple but with spaces between the values? There's this example on page 164: import std.typecons, std.stdio; void fun(T...)(T args) { // create a tuple to pack all arguments together gun(tuple(args)); } void gun(T)(T value) { // expand the tuple back writeln(value.expand); } void main() { fun(1); fun(1, 2.2); } This prints out: 1 12.2 But that's confusing, since the values are "glued" together when printed out. I really want to print out: 1 1 2.2 Any ideas?You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.One other thing. I can use the .length property for value and parameter tuples, but only if I haven't built them myself with the call to std.typecons.tuple(). For example I can call writeln(T.length) in the fun() function (where the compiler automatically constructs a tuple), but not in the gun() function. So there seems to be compiler tuples and user-constructed tuples, which are not the same.Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);It's a bit of a shame that there isn't a chapter on tuples except this brief mention (I don't see it listed in the contents page). I guess I'll take a look at the implementation.Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
Speaking of which, I think there's a typo in Phobos docs: http://www.digitalmars.com/d/2.0/phobos/std_typecons.html Tuple!(int, int) point; // assign coordinates point.field[0] = 5; point.field[1] = 6; // read coordinates auto x = point.field[0]; auto y = point.[1]; The last one should be "auto y = point.field[1]" On Fri, Jul 30, 2010 at 8:53 PM, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:I've found that the syntax mytuple.field[] can be used, I think this is probably what you meant by .first and .second. On Fri, Jul 30, 2010 at 8:20 PM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:Hey, just tried out your code. In the first one there's a little typo (used 'i' instead of index), but that's ok. ;p It seems like I don't need to use the to!() template. This code seems to work fine: void gun(T)(T value) { foreach(Type; value.expand) write(Type, " "); writeln(); } But regardless, at least I now know how to traverse through my own tuples, so thanks for that one. In your second example: Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14); I get an error way deep in the library itself (this could call for a better error message, methinks): C:\DMD\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(320): Error: tuple index 2 exceeds 2 I've reversed the order, put types before identifiers and this works fine then: Tuple!(int, "index", double, "sum") t = tuple(1, 3.14); I'm not sure what you mean by "t has .first and .second as members", I can't seem to access those. Maybe you meant t.index and t.sum as members? On Fri, Jul 30, 2010 at 12:11 PM, Philippe Sigaud < philippe.sigaud gmail.com> wrote:On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:How do I print out an expanded tuple but with spaces between the values? There's this example on page 164: import std.typecons, std.stdio; void fun(T...)(T args) { // create a tuple to pack all arguments together gun(tuple(args)); } void gun(T)(T value) { // expand the tuple back writeln(value.expand); } void main() { fun(1); fun(1, 2.2); } This prints out: 1 12.2 But that's confusing, since the values are "glued" together when printed out. I really want to print out: 1 1 2.2 Any ideas?You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.One other thing. I can use the .length property for value and parameter tuples, but only if I haven't built them myself with the call to std.typecons.tuple(). For example I can call writeln(T.length) in the fun() function (where the compiler automatically constructs a tuple), but not in the gun() function. So there seems to be compiler tuples and user-constructed tuples, which are not the same.Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);It's a bit of a shame that there isn't a chapter on tuples except this brief mention (I don't see it listed in the contents page). I guess I'll take a look at the implementation.Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
On Fri, 30 Jul 2010, Andrej Mitrovic wrote:Speaking of which, I think there's a typo in Phobos docs: http://www.digitalmars.com/d/2.0/phobos/std_typecons.html Tuple!(int, int) point; // assign coordinates point.field[0] = 5; point.field[1] = 6; // read coordinates auto x = point.field[0]; auto y = point.[1]; The last one should be "auto y = point.field[1]"Fix submitted to svn.
Jul 30 2010
On Fri, Jul 30, 2010 at 20:20, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:Hey, just tried out your code. In the first one there's a little typo (used 'i' instead of index), but that's ok. ;pAh yes, that was typed over the course of 2-3 hours, with my children jumping around :)It seems like I don't need to use the to!() template. This code seems to work fine: void gun(T)(T value) { foreach(Type; value.expand) write(Type, " "); writeln(); }If you just want to print them, yes. In that case btw, do not use 'Type' as a name, as you're directly iterating on the values. Sorry, my mistake: I'm so used to iterate on typetuples, where the iteration is done on a index-Type pair that I typed that without thinking.But regardless, at least I now know how to traverse through my own tuples, so thanks for that one.As I said, value.expand is an instantiated typetuple, to it's iterable, indexed with [i], can be sliced with [i..j], it knows its length, etc. Very powerful stuff, very easy to use. None of the gobbledygook from C++. Note that .field and .expand are aliases, they are the same access to the tuple's fields, under a different name.In your second example: Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14); I get an error way deep in the library itself (this could call for a better error message, methinks): C:\DMD\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(320): Error: tuple index 2 exceeds 2 I've reversed the order, put types before identifiers and this works fine then: Tuple!(int, "index", double, "sum") t = tuple(1, 3.14);Sorry also, I should have checked typecons docs.I'm not sure what you mean by "t has .first and .second as members", I can't seem to access those. Maybe you meant t.index and t.sum as members?Hem, as I said, I typed the mail over a few hours, the first version of the tuple was Tuple!((int, "first", double, "second"), but I found first and second a bit bland, so I changed that to .index and .sum. Man, I made a mess of my explanation. I'm glad you still understood something :)
Jul 30 2010
On page 143 there's a new find() overloaded function which is more specialized than the last generic function, but I'm still getting an ambiguity error. Although the book doesn't state if that specific unittest should work, so I'm left wondering :) Here's the code: void main() { } T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) { while (longer.length >= shorter.length) { if (longer[0 .. shorter.length] == shorter) break; longer = longer[1 .. $]; } return longer; } int[] find(int[] longer, int[] shorter) { } unittest { int[] ints1 = [1, 2, 3, 5, 2] ; int[] ints2 = [3, 5]; auto test = find(ints1, ints2); } test.d(16): Error: function test.find conflicts with template test.find(T1,T2) if (is(typeof(longer[0..1] == shorter) : bool)) at test.d(5) And on page 145 the last example is: void main() { } void transmogrify(uint) { } void transmogrify(long) { } void transmogrify(T)(T value) { } unittest { transmogrify(42); transmogrify("hello"); transmogrify(1.1); } test2.d(5): Error: template test2.transmogrify(T) conflicts with function test2.transmogrify at test2.d(3) On Wed, Jul 28, 2010 at 7:32 PM, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:Woops, I got confused. I was thinking about structs, not arrays. But yeah, in this case a float gets compared to a double and it seems to evaluate to false. On Wed, Jul 28, 2010 at 7:26 PM, Jonathan M Davis <jmdavisprog gmail.com>wrote:On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:IIRC arrays are value types and as such they are compared bit-by-bit to each other, right?Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
Page 149, the two shorter-hand ways of writing function literals are not working for me: void main() { } T[] find(alias pred, T)(T[] input) if (is(typeof(pred(input[0])) == bool)) { for (; input.length > 0; input = input[1 .. $]) { if (pred(input[0])) break; } return input; } unittest { int[] a = [1, 2, 3, 4, -5, 3, -4]; auto b = find!(function bool(int x) { return x < 0; })(a); //~ auto c = find!(function(x) { return x < 0; })(a); //~ auto d = find!((x) { return x < 0; })(a); } The first one works fine. But if I uncomment the second or third line I get these errors: higher_order_functions.d(17): Error: template higher_order_functions.find(alias pred,T) if (is(typeof(pred(input[0])) == bool)) does not match any function template declaration higher_order_functions.d(17): Error: template higher_order_functions.find(alias pred,T) if (is(typeof(pred(input[0])) == bool)) cannot deduce template function from argument types !(__funcliteral1)(int[]) On Wed, Jul 28, 2010 at 10:44 PM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:On page 143 there's a new find() overloaded function which is more specialized than the last generic function, but I'm still getting an ambiguity error. Although the book doesn't state if that specific unittest should work, so I'm left wondering :) Here's the code: void main() { } T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) { while (longer.length >= shorter.length) { if (longer[0 .. shorter.length] == shorter) break; longer = longer[1 .. $]; } return longer; } int[] find(int[] longer, int[] shorter) { } unittest { int[] ints1 = [1, 2, 3, 5, 2] ; int[] ints2 = [3, 5]; auto test = find(ints1, ints2); } test.d(16): Error: function test.find conflicts with template test.find(T1,T2) if (is(typeof(longer[0..1] == shorter) : bool)) at test.d(5) And on page 145 the last example is: void main() { } void transmogrify(uint) { } void transmogrify(long) { } void transmogrify(T)(T value) { } unittest { transmogrify(42); transmogrify("hello"); transmogrify(1.1); } test2.d(5): Error: template test2.transmogrify(T) conflicts with function test2.transmogrify at test2.d(3) On Wed, Jul 28, 2010 at 7:32 PM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:Woops, I got confused. I was thinking about structs, not arrays. But yeah, in this case a float gets compared to a double and it seems to evaluate to false. On Wed, Jul 28, 2010 at 7:26 PM, Jonathan M Davis <jmdavisprog gmail.com>wrote:On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:IIRC arrays are value types and as such they are compared bit-by-bit to each other, right?Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
They do work if I specify the type before x, as in: auto c = find!(function(int x) { return x < 0; })(a); auto d = find!((int x) { return x < 0; })(a); On Wed, Jul 28, 2010 at 11:01 PM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:Page 149, the two shorter-hand ways of writing function literals are not working for me: void main() { } T[] find(alias pred, T)(T[] input) if (is(typeof(pred(input[0])) == bool)) { for (; input.length > 0; input = input[1 .. $]) { if (pred(input[0])) break; } return input; } unittest { int[] a = [1, 2, 3, 4, -5, 3, -4]; auto b = find!(function bool(int x) { return x < 0; })(a); //~ auto c = find!(function(x) { return x < 0; })(a); //~ auto d = find!((x) { return x < 0; })(a); } The first one works fine. But if I uncomment the second or third line I get these errors: higher_order_functions.d(17): Error: template higher_order_functions.find(alias pred,T) if (is(typeof(pred(input[0])) == bool)) does not match any function template declaration higher_order_functions.d(17): Error: template higher_order_functions.find(alias pred,T) if (is(typeof(pred(input[0])) == bool)) cannot deduce template function from argument types !(__funcliteral1)(int[]) On Wed, Jul 28, 2010 at 10:44 PM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:On page 143 there's a new find() overloaded function which is more specialized than the last generic function, but I'm still getting an ambiguity error. Although the book doesn't state if that specific unittest should work, so I'm left wondering :) Here's the code: void main() { } T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) { while (longer.length >= shorter.length) { if (longer[0 .. shorter.length] == shorter) break; longer = longer[1 .. $]; } return longer; } int[] find(int[] longer, int[] shorter) { } unittest { int[] ints1 = [1, 2, 3, 5, 2] ; int[] ints2 = [3, 5]; auto test = find(ints1, ints2); } test.d(16): Error: function test.find conflicts with template test.find(T1,T2) if (is(typeof(longer[0..1] == shorter) : bool)) at test.d(5) And on page 145 the last example is: void main() { } void transmogrify(uint) { } void transmogrify(long) { } void transmogrify(T)(T value) { } unittest { transmogrify(42); transmogrify("hello"); transmogrify(1.1); } test2.d(5): Error: template test2.transmogrify(T) conflicts with function test2.transmogrify at test2.d(3) On Wed, Jul 28, 2010 at 7:32 PM, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote:Woops, I got confused. I was thinking about structs, not arrays. But yeah, in this case a float gets compared to a double and it seems to evaluate to false. On Wed, Jul 28, 2010 at 7:26 PM, Jonathan M Davis <jmdavisprog gmail.comwrote:On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:IIRC arrays are value types and as such they are compared bit-by-bit to each other, right?Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
Okay take a look at this: import std.stdio; void main() { } T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool)) { while (haystack.length > 0 && haystack[0] != needle) { haystack = haystack[1 .. $]; } return haystack; } T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool) && false) { while (longer.length >= shorter.length) { if (longer[0 .. shorter.length] == shorter) break; longer = longer[1 .. $]; } return longer; } unittest { string[] longer = ["one", "two"]; string shorter = "two"; find(longer, shorter); writeln(is(typeof(longer[0 .. 1] == shorter) : bool) && false); // writes false } I'm tyring to make the code in the unittest use the first templated function. But it will fail because DMD matches the call to find to the second templated function. But it should never match the second template, I've forced it's constraint to always evaluate to false. The writeln() in the unittest has the copy of the constraint expression and it does evaluate to false. If I comment out the second template, this code will compile as DMD will match the first template. I'm using this forced "&& false" expression because I was trying out all sorts of type/value expressions but they never worked for me. Anyways, I'd appreciate any help here. :) On Wed, Jul 28, 2010 at 5:22 PM, Andrej Mitrovic <andrej.mitrovich gmail.comwrote:These templates seem to be hard to get right. I guess with experience they get easier to write and comprehend. Anyways, thanks for the explanation. On Wed, Jul 28, 2010 at 4:44 PM, Philippe Sigaud < philippe.sigaud gmail.com> wrote:On Wed, Jul 28, 2010 at 05:50, Andrej Mitrovic < andrej.mitrovich gmail.com> wrote: I won't comment on the double/float issue, I do not anything about it.T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool)) T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) Also, Andrei, you never explained the if(is()) signature of the second templated function. I hope to get some pointers on that. :)I'll have a try at this one. As you may know, the is(typeof()) syntax is a way to try to compile an expression and see if it works. If it works, it has a type, given by typeof and is(Type) returns true. So the first one is really saying: "Hey compiler, may I compare an element of haystack (of type T) with an E In this case, longer is an array. So longer[0] would be an element, a T1. There is no chance that shorter will match as an element cannot be equal to an array ... except, of course, if longer is an array of T2[] (T1 == T2[]). Longer is a T2[][]. That may happen for arrays of strings, strings being arrays of chars. And I think that's the problem you have in your last unit test. Anyway, he takes longer[0..1] to get a slice, which is a dynamic array, a T1[] and hence comparable to a T2[]... most of the time. As for using ': bool' instead of '== bool' as in the first find(), I don't think there is any difference in this case. I understand T == U as 'T is the exact same type as U', whereas T : U is for me "T is a subtype of U". But then, a subtype of bool is pretty much constrained to be a bool. The T : U syntax is pretty much only used for classes : if(is(T : MyClass)) is saying : 'compile this only if T is a subclass of MyClass'. Philippe , except if longer is in fact
Jul 30 2010
First, trying a copy-paste of your code gives me an error in object.di (DMD 2.047 on Windows) C:\dmd\windows\bin\..\..\src\druntime\import\object.di|487|Error: incompatible types for ((a) != (a2[i])): 'string' and 'immutable(char)'| ||=== Build finished: 1 errors, 0 warnings ===| And Code::Blocks jumps me to object.di: bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2) { if (a1.length != a2.length) return false; foreach(i, a; a1) { if (a != a2[i]) // <-- line 487. Uh oh, trying to compare strings and chars return false; } return true; } I had to modify it: bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2) if (is(typeof(T1.init != T2.init))) // Are T1 and T2 comparable? If not, do not bother to compile { if (a1.length != a2.length) return false; foreach(i, a; a1) { if (a != a2[i]) return false; } return true; } Another possibility would have it return false, but in that case, array comparisons always compile and we lose the ability to use them as test for instantiating a template. I guess _ArrayEq is some internal called by the constraint, while doing the comparison. Saving object.di and recompiling everything allows the templates constraints to function and gives me a call to the first find. Without the '&& false', it still calls the first find... because it still cannot instantiate the second template: The first find can be instantiated with T = immutable(char)[] and E = immutable(char)[], there is nothing stopping it: T and E are the same type, so they are trivially comparable. "two" is a valid target value for ["one", "two"]. The second find would have T1 = immutable(char)[] and T2 = immutable(char) and those do not compare. testing it, find("one two three", "two") correctly calls the second version and returns "two three". So, maybe you just found a bug in object.di._ArrayEq. I'll post on the main list. Anyway, for me, it seems to work, if I understand correctly what you're doing. Btw, if you find yourself using the same constraint regularly, you can abstract it away in another template that returns (er, becomes, rather) a boolean: template areComparable(T1, T2) { enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous template trick } And then: T[] find(T, E)(T[] haystack, E needle) if (areComparable!(T, E)) { } T[] find(T, E)(T[] longer, E[] shorter) if (areComparable!(T, E)) { } Which makes for cleaner code. I renamed T1 and T2 to T and E, so showcase the difference between the two. Philippe
Jul 30 2010
On Sat, Jul 31, 2010 at 12:04 AM, Philippe Sigaud <philippe.sigaud gmail.comwrote:First, trying a copy-paste of your code gives me an error in object.di (DMD 2.047 on Windows)Yes, I get the same error.C:\dmd\windows\bin\..\..\src\druntime\import\object.di|487|Error: incompatible types for ((a) != (a2[i])): 'string' and 'immutable(char)'| ||=== Build finished: 1 errors, 0 warnings ===| And Code::Blocks jumps me to object.di: bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2) { if (a1.length != a2.length) return false; foreach(i, a; a1) { if (a != a2[i]) // <-- line 487. Uh oh, trying to compare strings and chars return false; } return true; } I had to modify it: bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2) if (is(typeof(T1.init != T2.init))) // Are T1 and T2 comparable? If not, do not bother to compile { if (a1.length != a2.length) return false; foreach(i, a; a1) { if (a != a2[i]) return false; } return true; } Another possibility would have it return false, but in that case, array comparisons always compile and we lose the ability to use them as test for instantiating a template. I guess _ArrayEq is some internal called by the constraint, while doing the comparison. Saving object.di and recompiling everything allows the templates constraints to function and gives me a call to the first find. Without the '&& false', it still calls the first find... because it still cannot instantiate the second template: The first find can be instantiated with T = immutable(char)[] and E = immutable(char)[], there is nothing stopping it: T and E are the same type, so they are trivially comparable. "two" is a valid target value for ["one", "two"]. The second find would have T1 = immutable(char)[] and T2 = immutable(char) and those do not compare. testing it, find("one two three", "two") correctly calls the second version and returns "two three".So, maybe you just found a bug in object.di._ArrayEq. I'll post on the mainlist. Anyway, for me, it seems to work, if I understand correctly what you're doing.What I am trying to do is to get the code sample from TDPL working. I tried to constraint the second template so it never gets called with: assert(find(s, "two") == ["two"]); because this was in the unittest right under the first template, and I assumed it should work only with the first template. On TDPL page 143 it states that there is no ambiguity between the two templates, so this entire following code should work (could you please try this out with your new object.di if you have any time?). I hope Andrei won't mind for pasting this here: import std.stdio; void main() { } T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool)) { while (haystack.length > 0 && haystack[0] != needle) { haystack = haystack[1 .. $]; } return haystack; } unittest { double[] d = [1.5, 2.4]; assert(find(d, 1.0) == null); assert(find(d, 1.5) == d); string[] s = ["one", "two"]; assert(find(s, "two") == ["two"]); } T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) { while (longer.length >= shorter.length) { if (longer[0 .. shorter.length] == shorter) break; longer = longer[1 .. $]; } return longer; } unittest { double[] d1 = [6.0, 1.5, 2.4, 3]; float[] d2 = [1.5, 2.4]; assert(find(d1, d2) == d1[1 .. $]); }Btw, if you find yourself using the same constraint regularly, you can abstract it away in another template that returns (er, becomes, rather) a boolean: template areComparable(T1, T2) { enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous template trick } And then: T[] find(T, E)(T[] haystack, E needle) if (areComparable!(T, E)) { } T[] find(T, E)(T[] longer, E[] shorter) if (areComparable!(T, E)) { } Which makes for cleaner code. I renamed T1 and T2 to T and E, so showcase the difference between the two. PhilippeI was actually thinking about ways of simplifying the constraint definitions with some helper functions. That's a pretty cool trick, thanks!
Jul 30 2010
I think there really is some bug here. I tried to use your template helper function, and it seems the order in which I put two constraints in a template signature changes the way dmd compiles the code. Consider this: import std.stdio; void main() { } template areComparable(T1, T2) { enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous template trick } T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool) && areComparable!(T, E)) { while (haystack.length > 0 && haystack[0] != needle) { haystack = haystack[1 .. $]; } return haystack; } T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool) && (areComparable!(T1, T2))) { while (longer.length >= shorter.length) { if (longer[0 .. shorter.length] == shorter) break; longer = longer[1 .. $]; } return longer; } unittest { double[] d = [1.5, 2.4]; assert(find(d, 1.0) == null); assert(find(d, 1.5) == d); string[] s = ["one", "two"]; assert(find(s, "two") == ["two"]); } This will give out the error "incompatible types for comparing of string and immutable(char)". But if I reverse the order of the constraints in the second template, like so: import std.stdio; void main() { } template areComparable(T1, T2) { enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous template trick } T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool) && areComparable!(T, E)) { while (haystack.length > 0 && haystack[0] != needle) { haystack = haystack[1 .. $]; } return haystack; } T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (areComparable!(T1, T2) && is(typeof(longer[0 .. 1] == shorter) : bool)) { while (longer.length >= shorter.length) { if (longer[0 .. shorter.length] == shorter) break; longer = longer[1 .. $]; } return longer; } unittest { double[] d = [1.5, 2.4]; assert(find(d, 1.0) == null); assert(find(d, 1.5) == d); string[] s = ["one", "two"]; assert(find(s, "two") == ["two"]); } Then this happily compiles. All of the asserts will call the first templated function. On Sat, Jul 31, 2010 at 12:04 AM, Philippe Sigaud <philippe.sigaud gmail.comwrote:First, trying a copy-paste of your code gives me an error in object.di (DMD 2.047 on Windows) C:\dmd\windows\bin\..\..\src\druntime\import\object.di|487|Error: incompatible types for ((a) != (a2[i])): 'string' and 'immutable(char)'| ||=== Build finished: 1 errors, 0 warnings ===| And Code::Blocks jumps me to object.di: bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2) { if (a1.length != a2.length) return false; foreach(i, a; a1) { if (a != a2[i]) // <-- line 487. Uh oh, trying to compare strings and chars return false; } return true; } I had to modify it: bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2) if (is(typeof(T1.init != T2.init))) // Are T1 and T2 comparable? If not, do not bother to compile { if (a1.length != a2.length) return false; foreach(i, a; a1) { if (a != a2[i]) return false; } return true; } Another possibility would have it return false, but in that case, array comparisons always compile and we lose the ability to use them as test for instantiating a template. I guess _ArrayEq is some internal called by the constraint, while doing the comparison. Saving object.di and recompiling everything allows the templates constraints to function and gives me a call to the first find. Without the '&& false', it still calls the first find... because it still cannot instantiate the second template: The first find can be instantiated with T = immutable(char)[] and E = immutable(char)[], there is nothing stopping it: T and E are the same type, so they are trivially comparable. "two" is a valid target value for ["one", "two"]. The second find would have T1 = immutable(char)[] and T2 = immutable(char) and those do not compare. testing it, find("one two three", "two") correctly calls the second version and returns "two three". So, maybe you just found a bug in object.di._ArrayEq. I'll post on the main list. Anyway, for me, it seems to work, if I understand correctly what you're doing. Btw, if you find yourself using the same constraint regularly, you can abstract it away in another template that returns (er, becomes, rather) a boolean: template areComparable(T1, T2) { enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous template trick } And then: T[] find(T, E)(T[] haystack, E needle) if (areComparable!(T, E)) { } T[] find(T, E)(T[] longer, E[] shorter) if (areComparable!(T, E)) { } Which makes for cleaner code. I renamed T1 and T2 to T and E, so showcase the difference between the two. Philippe
Jul 31 2010
On Sat, Jul 31, 2010 at 17:24, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:I think there really is some bug here. I tried to use your template helper function, and it seems the order in which I put two constraints in a template signature changes the way dmd compiles the code. Consider this:T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool) && areComparable!(T, E)) ...T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool) && (areComparable!(T1, T2)))This will give out the error "incompatible types for comparing of string and immutable(char)".That's because the first part of the constraint is tested, to allow for short-circuiting the second part. So the bad array comparison triggers the problem in object._EqArray. You should always test for the most general constraint first, I guess. In this particular case, use areComparable!(A,B) as the first argument to && in both templates.But if I reverse the order of the constraints in the second template, like so: T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (areComparable!(T1, T2) && is(typeof(longer[0 .. 1] == shorter) : bool))Then this happily compiles.Well, yes. The second template cannot be instantiated: T1 is a string, T2 is a character. So isComparable evaluates to 'false' and the constraints short-circuits to 'false'.All of the asserts will call the first templated function.Because you are looking for an element in a sequence of element in all three cases. That's what the first version of find() do. If you test: assert(find(d, [1.5]); // [1.5] is a one-element array. assert(find(d, [1.5, 2.4]); // finding d in d. assert(find("one two three", "two"); it will activate the second version for all these new asserts: you are looking for a sequence of elements in a sequence of elements, which is what the second find does. Philippe
Aug 01 2010
On Sat, Jul 31, 2010 at 00:58, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:could you please try this out with your new object.di if you have any time?). I hope Andrei won't mind for pasting this here: (snip)unittest { double[] d1 = [6.0, 1.5, 2.4, 3]; float[] d2 = [1.5, 2.4]; assert(find(d1, d2) == d1[1 .. $]); // unit test failure on line 40. }The assert fails, because find(d1, d2) does not returns [1.5, 2.4, 3] in my case. That's because floating points types are notoriously difficult to test for equality: double d = 2.4; float f = 2.4; writeln(d == f); // false! 2.4 do not have the same representation as a float or as a double. So I guess there must have been some pb with the testing Andrei has done on the examples, because this particular example do not work. To test floating point values, use std.math.approxEqual. For a function like find() you can imagine either using approxEqual as the comparator when you detect T/E, T1/T2 to be floating point values, or defer to the user to give you a valid comparator, as an alias: import std.functional: binaryFun; // template transforming strings like "a ==b" into functions. T1[] find(alias comparator = "a == b", T1, T2)(T1[] longer, T2[] shorter) if (is(typeof( binaryFun!comparator(longer[0 .. 1], shorter)) : bool)) { while (longer.length >= shorter.length) { if (binaryFun!comparator(longer[0 .. shorter.length], shorter)) break; longer = longer[1 .. $]; } return longer; } usage: void main() { double[] d = [1.0, 1.5, 2.4, 3.14]; float[] f = [1.5, 2.4]; writeln(find(d, f)); // use a == b as a comparator, so returns 3.14, did not see 1.5 float and 1.5 double as equal writeln(find!approxEqual(d, f)); // [1.5, 2.4, 3.14] } btw, the behavior is not correct: find(d,f) should return an empty array, not [3.14]... I was actually thinking about ways of simplifying the constraint definitionswith some helper functions. That's a pretty cool trick, thanks!It's used throughout Phobos, particularly in the template-heavy modules, like std.algorithm or std.range. Philippe
Aug 01 2010
On Sun, Aug 1, 2010 at 11:15 PM, Philippe Sigaud <philippe.sigaud gmail.com>wrote:On Sat, Jul 31, 2010 at 17:24, Andrej Mitrovic <andrej.mitrovich gmail.comLet me see if I'm getting this right. false && true will short circuit to false (this is normal). true && false will short circuit to true? How can that be? It's not a logical OR, its an AND, and as far as I know both expressions need to be True to evaluate to True. Or is it different for template constraints? Thanks again for the std.math.approxEqual and binaryFun tips.wrote:I think there really is some bug here. I tried to use your template helper function, and it seems the order in which I put two constraints in a template signature changes the way dmd compiles the code. Consider this:T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool) && areComparable!(T, E)) ...T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool) && (areComparable!(T1, T2)))This will give out the error "incompatible types for comparing of string and immutable(char)".That's because the first part of the constraint is tested, to allow for short-circuiting the second part. So the bad array comparison triggers the problem in object._EqArray. You should always test for the most general constraint first, I guess. In this particular case, use areComparable!(A,B) as the first argument to && in both templates.
Aug 01 2010
On Mon, Aug 2, 2010 at 00:37, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:That's because the first part of the constraint is tested, to allow forI was answering you about the "incompatible types" error. The first part of the template, 'is(typeof(longer[0 .. 1] == shorter) : bool', triggers the error and that stops the compilation process. I agree that, if longer[0..1] == shorter is an error, then we could have is(typeof()) result in 'true'. But here what happens is that everything stops. I'll post about this on the main list, see if it's normal behavior or not and add a bug report. Philippeshort-circuiting the second part. So the bad array comparison triggers the problem in object._EqArray. You should always test for the most general constraint first, I guess. In this particular case, use areComparable!(A,B) as the first argument to && in both templates.Let me see if I'm getting this right. false && true will short circuit to false (this is normal). true && false will short circuit to true? How can that be? It's not a logical OR, its an AND, and as far as I know both expressions need to be True to evaluate to True. Or is it different for template constraints?
Aug 01 2010