View mode: basic / threaded / horizontal-split · Log in · Help
July 28, 2012
sum across an array of objects
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.
July 28, 2012
Re: sum across an array of objects
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
July 28, 2012
Re: sum across an array of objects
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
July 28, 2012
Re: sum across an array of objects
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
July 28, 2012
Re: sum across an array of objects
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.
July 28, 2012
Re: sum across an array of objects
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.
July 28, 2012
Re: sum across an array of objects
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
July 28, 2012
Re: sum across an array of objects
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
> 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.

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.
Top | Discussion index | About this forum | D home