www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - sum across an array of objects

reply "Philip Daniels" <Philip.Daniels1971 gmail.com> writes:
I have an array of objects that have a size() property. Code to 
sum them manually works fine, as expected:

   auto total = 0;
   foreach (wt; _word_tables)
     total += wt.size();
   return total;


How can I do this with reduce? (BTW I second the old comments and 
bugs about not having sum() built into the library, it's 
annoying.)

I tried

   auto total = reduce!(
     (int a, WordTable b) { return a + b.size(); })
     (0, _word_tables);

but it seems that reduce expects a and b to be of type WordTable, 
so it won't instantiate the template. It's also as long as the 
manual version :-(

I really want to do

   auto total = sum(a => a.size(), _word_tables)

if that's possible.
Jul 28 2012
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, July 28, 2012 09:58:32 Philip Daniels wrote:
 I have an array of objects that have a size() property. Code to
 sum them manually works fine, as expected:
 
    auto total = 0;
    foreach (wt; _word_tables)
      total += wt.size();
    return total;
 
 
 How can I do this with reduce? (BTW I second the old comments and
 bugs about not having sum() built into the library, it's
 annoying.)
 
 I tried
 
    auto total = reduce!(
      (int a, WordTable b) { return a + b.size(); })
      (0, _word_tables);
 
 but it seems that reduce expects a and b to be of type WordTable,
 so it won't instantiate the template. It's also as long as the
 manual version :-(
 
 I really want to do
 
    auto total = sum(a => a.size(), _word_tables)
 
 if that's possible.

How about import std.algorithm; import std.array; import std.stdio; void main() { auto stuff = ["hello world", "Goodbye, Shirley", "Babylon 5 rocks"]; auto result = reduce!"a + b.length"(0L, stuff); assert(result == join(stuff).length); } The first parameter is the sum, and the second is the item. So, I expect that auto total = reduce!((a, b) => a + b.size())(0, _word_tables); would work for you. - Jonathan M Davis
Jul 28 2012
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/28/2012 06:49 AM, Philip Daniels wrote:

 It seems to
 be something to do with whether the array is static or dynamic.

Good catch! :) Welcome to D where static arrays (aka "fixed-length arrays") and dynamic arrays (aka "slices") are different beasts. A number of factors are at play here: 1) Static arrays are value types. As a result, the entire array gets copied when pass to a function (e.g. reduce) as an argument. 2) The length of static arrays cannot change. 3) Phobos is a range-based library. reduce() is an algorithm that not more than an InputRange.[*] InputRanges are naturally consumed as they are being iterated over. For the reasons above, a static array is not usable by reduce(). Your solution of taking a slice of all of the elements first by _word_tables[] is the correct solution (and very cheap, thanks to D's slices). Ali [*] Arguably, reduce() could have a specialization that worked on RandomAccessRanges (static arrays included), making life easier. I don't see why that could not be added to Phobos.
Jul 28 2012
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/28/2012 07:36 AM, Ali Çehreli wrote:

 reduce() is an algorithm that not more than an InputRange.

That should be: reduce() is an algorithm that _does not require_ more than an InputRange. (That is also misleading: reduce() requires an "iterable object", which can be any type of a range, as well as an object of a type that defines the opApply() member function.) Ali
Jul 28 2012
prev sibling next sibling parent "Philip Daniels" <Philip.Daniels1971 gmail.com> writes:
On Saturday, 28 July 2012 at 08:32:57 UTC, Jonathan M Davis wrote:
 void main()
 {
     auto stuff = ["hello world",
                   "Goodbye, Shirley",
                   "Babylon 5 rocks"];

     auto result = reduce!"a + b.length"(0L, stuff);
     assert(result == join(stuff).length);
 }

 The first parameter is the sum, and the second is the item. So, 
 I expect that

 auto total = reduce!((a, b) => a + b.size())(0, _word_tables);

 would work for you.


 - Jonathan M Davis

Jonathan, I tried the above suggestion, and also this equivalent based on your code: auto total = reduce!"a + b.size()"(0, _word_tables); in both cases I get /usr/include/i386-linux-gnu/dmd/phobos/std/conv.d(3330): Error: cannot implicitly convert expression (_param_1) of type WordDatabase.WordTable to int /usr/include/i386-linux-gnu/dmd/phobos/std/algorithm.d(743): Error: template instance std.conv.emplace!(int,WordTable) error instantiating WordDatabase.d(150): instantiated from here: reduce!(int,WordTable[30u]) WordDatabase.d(150): Error: template instance std.algorithm.reduce!("a + b.size()").reduce!(int,WordTable[30u]) error instantiating
Jul 28 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Philip Daniels:

 cannot implicitly convert expression (_param_1) of type 
 WordDatabase.WordTable to int

That error tells you to not use an int as first value for the reduce. Maybe this is enough to solve your problem. Bye, bearophile
Jul 28 2012
prev sibling next sibling parent "Philip Daniels" <Philip.Daniels1971 gmail.com> writes:
On Saturday, 28 July 2012 at 10:58:45 UTC, bearophile wrote:
 Philip Daniels:

 cannot implicitly convert expression (_param_1) of type 
 WordDatabase.WordTable to int

That error tells you to not use an int as first value for the reduce. Maybe this is enough to solve your problem. Bye, bearophile

Well...sort of, but I am not sure why. I was able to get to the right syntax by first doing it in parts, using map! and reduce! on separate lines, which then gave me a pointer to the original problem. It seems to be something to do with whether the array is static or dynamic. If the array is static, which mine was, then this form will work - note the extra [] on _word_tables: auto total = reduce!"a + b.size()"(0L, _word_tables[]); But if the array is dynamic then both of these forms work auto total = reduce!"a + b.size()"(0L, _word_tables); auto total = reduce!"a + b.size()"(0L, _word_tables[]); That seems counter-intuitive to me at the moment, coming brand new to the language. I know that the [] meaans "take a slice of the array" but I don't see why that should be considered different, since it is the entire array.
Jul 28 2012
prev sibling parent "Philip Daniels" <Philip.Daniels1971 gmail.com> writes:
On Saturday, 28 July 2012 at 14:36:26 UTC, Ali Çehreli wrote:
 On 07/28/2012 06:49 AM, Philip Daniels wrote:

 It seems to
 be something to do with whether the array is static or

Good catch! :) Welcome to D where static arrays (aka "fixed-length arrays") and dynamic arrays (aka "slices") are different beasts. A number of factors are at play here: 1) Static arrays are value types. As a result, the entire array gets copied when pass to a function (e.g. reduce) as an argument. 2) The length of static arrays cannot change. 3) Phobos is a range-based library. reduce() is an algorithm that not more than an InputRange.[*] InputRanges are naturally consumed as they are being iterated over. For the reasons above, a static array is not usable by reduce(). Your solution of taking a slice of all of the elements first by _word_tables[] is the correct solution (and very cheap, thanks to D's slices). Ali [*] Arguably, reduce() could have a specialization that worked on RandomAccessRanges (static arrays included), making life easier. I don't see why that could not be added to Phobos.

Thanks for the explanation Ali. I was planning on converting the array to dynamic anyway, since the ultimate size is dependent upon the program's input, so I think I'll do that now.
Jul 28 2012