digitalmars.D.learn - Allowing arbitrary types for a function's argument and return type
- pineapple (34/34) Oct 22 2015 I'm just starting to hammer D's very pleasant syntax into my
- Adam D. Ruppe (24/43) Oct 22 2015 D's templates are easy (you actually used one in there, the
- pineapple (3/15) Oct 22 2015 Wonderful, thanks much! It keeps surprising me how easy it is to
- John Colvin (23/57) Oct 22 2015 Using ranges instead of threads or fibers, slightly
- pineapple (10/12) Oct 22 2015 What does if(isIntegral!T) do? It looks like it would verify that
- John Colvin (12/24) Oct 22 2015 Only with builtin types:
- Kagamin (8/10) Oct 22 2015 It doesn't verify, but filters: you can have several templates
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",&i);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!
Oct 22 2015
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.
Oct 22 2015
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.
Oct 22 2015
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",&i);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))); }
Oct 22 2015
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?
Oct 22 2015
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: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.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?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?:)
Oct 22 2015
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.
Oct 22 2015