Re: Allowing arbitrary types for a function's argument and return type
On Thursday, 22 October 2015 at 15:10:58 UTC, pineapple wrote: What does if(isIntegral!T) do? It looks like it would verify that the template type is a discrete number? It doesn't verify, but filters: you can have several templates with the same name, when filter doesn't match, compiler tries another template. Maybe you can write collatzStep for string type, then this code wouldn't compile, because it doesn't make sense for strings. Remove the filter and the template will always compile with whatever it's supplied including BigNum as long as the code makes sense.
Re: Allowing arbitrary types for a function's argument and return type
On Thursday, 22 October 2015 at 13:58:56 UTC, Adam D. Ruppe wrote: D's templates are easy (you actually used one in there, the Generator is one!) Try this: import std.concurrency; Generator!T sequence(T)(T i){ return new Generator!T({ yield(i); while(i > 1){ yield(i = (i % 2) ? (i * 3 + 1) : (i >> 1)); } }); } Wonderful, thanks much! It keeps surprising me how easy it is to do stuff like this.
Re: Allowing arbitrary types for a function's argument and return type
On Thursday, 22 October 2015 at 13:53:33 UTC, pineapple wrote: I'm just starting to hammer D's very pleasant syntax into my head. After "Hello world", the first thing I do when learning any language is to write a simple program which generates and outputs the Collatz sequence for an arbitrary number. (I also like to golf it.) This is what I wrote in D: import std.stdio;void main(){int i;readf(" %d",);while(i>1){writeln(i=i%2?i*3+1:i/2);}} Any ways I could shorten it further? Anyway, then I thought I should try something that was less of a mess, too, and wrote this: import std.concurrency; Generator!int sequence(int i){ return new Generator!int({ yield(i); while(i > 1){ yield(i = (i % 2) ? (i * 3 + 1) : (i >> 1)); } }); } Which can be used like so: import std.stdio; void main(){ foreach(i; sequence(11)){ writeln(i); } } And now I'd like to make one more improvement, but this I haven't been able to figure out. What if I wanted the argument and output types to be longs instead of ints? Or some other, arbitrary discrete numeric type? Is there any template-like syntax I can use here instead of just copypasting for each numeric type I can think of? I've been spoiled by the likes of Python to be thinking in this duck-typing way. Thanks! Using ranges instead of threads or fibers, slightly over-engineered to show off features: import std.traits : isIntegral; auto collatzStep(T)(T i) if(isIntegral!T) { return (i % 2) ? (i * 3 + 1) : (i >> 1); } auto collatz(T)(T a) if(isIntegral!T) { import std.range : recurrence; import std.algorithm : until, OpenRight; return a.recurrence!((a, n) => collatzStep(a[n-1])) .until!(n => n == 1)(OpenRight.no); } unittest { import std.algorithm : equal; import std.range : only; assert(collatz(6L).equal(only(6, 3, 10, 5, 16, 8, 4, 2, 1))); }
Allowing arbitrary types for a function's argument and return type
I'm just starting to hammer D's very pleasant syntax into my head. After "Hello world", the first thing I do when learning any language is to write a simple program which generates and outputs the Collatz sequence for an arbitrary number. (I also like to golf it.) This is what I wrote in D: import std.stdio;void main(){int i;readf(" %d",);while(i>1){writeln(i=i%2?i*3+1:i/2);}} Any ways I could shorten it further? Anyway, then I thought I should try something that was less of a mess, too, and wrote this: import std.concurrency; Generator!int sequence(int i){ return new Generator!int({ yield(i); while(i > 1){ yield(i = (i % 2) ? (i * 3 + 1) : (i >> 1)); } }); } Which can be used like so: import std.stdio; void main(){ foreach(i; sequence(11)){ writeln(i); } } And now I'd like to make one more improvement, but this I haven't been able to figure out. What if I wanted the argument and output types to be longs instead of ints? Or some other, arbitrary discrete numeric type? Is there any template-like syntax I can use here instead of just copypasting for each numeric type I can think of? I've been spoiled by the likes of Python to be thinking in this duck-typing way. Thanks!
Re: Allowing arbitrary types for a function's argument and return type
On Thursday, 22 October 2015 at 13:53:33 UTC, pineapple wrote: import std.concurrency; Generator!int sequence(int i){ return new Generator!int({ yield(i); while(i > 1){ yield(i = (i % 2) ? (i * 3 + 1) : (i >> 1)); } }); } Which can be used like so: import std.stdio; void main(){ foreach(i; sequence(11)){ writeln(i); } } And now I'd like to make one more improvement, but this I haven't been able to figure out. What if I wanted the argument and output types to be longs instead of ints? D's templates are easy (you actually used one in there, the Generator is one!) Try this: import std.concurrency; Generator!T sequence(T)(T i){ return new Generator!T({ yield(i); while(i > 1){ yield(i = (i % 2) ? (i * 3 + 1) : (i >> 1)); } }); } The first set of args, `(T)`, are the template arguments. You can then use hat anywhere in teh function as a type placeholder. Now, when you call it, it can automatically deduce the types: foreach(i; sequence(11)){ // still works, uses int foreach(i; sequence(11L)){ // also works, uses long now Or you can tell your own args explicitly: foreach(i; sequence!long(11)){ // uses long The pattern is name!(template, args, ...)(regular, args...) The ! introduces template arguments. If there is just one simple argument - one consisting of a single word - you can leaves the parenthesis out.
Re: Allowing arbitrary types for a function's argument and return type
On Thursday, 22 October 2015 at 14:36:52 UTC, John Colvin wrote: Using ranges instead of threads or fibers, slightly over-engineered to show off features: What does if(isIntegral!T) do? It looks like it would verify that the template type is a discrete number? If I were to create my own class, say a BigNum as an example, how could I specify that the isIntegral condition should be met for it? Apart from the aesthetics, what are the functional differences between using recurrence and using a Generator? Will one be more efficient than the other? It's not fair how easy it is to incorporate unit tests in D. Now what excuse will I have when my code is buggy?
Re: Allowing arbitrary types for a function's argument and return type
On Thursday, 22 October 2015 at 15:10:58 UTC, pineapple wrote: On Thursday, 22 October 2015 at 14:36:52 UTC, John Colvin wrote: Using ranges instead of threads or fibers, slightly over-engineered to show off features: What does if(isIntegral!T) do? It looks like it would verify that the template type is a discrete number? If I were to create my own class, say a BigNum as an example, how could I specify that the isIntegral condition should be met for it? Only with builtin types: http://dlang.org/phobos/std_traits.html#isIntegral However there are a variety of ways you could mark special properties of types and have your own isIntegral-like template that recognised them. user-defined-attributes are one possibility, adding an `enum isIntegral = true;` is another. Apart from the aesthetics, what are the functional differences between using recurrence and using a Generator? Will one be more efficient than the other? ranges are likely to be more efficient because, apart from anything else, they are easier for the compiler to reason about and optimise. Both are good tools, but ranges are more widespread in D. It's not fair how easy it is to incorporate unit tests in D. Now what excuse will I have when my code is buggy? :)