1

There are articles and presentations about functional style programming in D (e.g. http://www.drdobbs.com/architecture-and-design/component-programming-in-d/240008321). I never used D before, but I'm interested in trying it. Is there a way to write code in D similar to this Python expression:

max(x*y for x in range(N) for y in range(x, N) if str(x*y) == str(x*y)[::-1])

Are there D constructs for generators or list (array) comprehensions?

Paul Jurczak
  • 7,008
  • 3
  • 47
  • 72

2 Answers2

4

Here's one possible solution, not particularly pretty:

iota(1,N)
    .map!(x =>
        iota(x,N)
        .map!(y => tuple(x,y)))
    .joiner
    .map!(xy => xy[0]*xy[1])
    .filter!(xy => equal(to!string(xy), to!string(xy).retro))
    .reduce!max;

So what this actually does is create a range from 1 to N, and map each element to a range of tuples with your x,y values. This gives you a nested range ([[(1,1),(1,2)],[(2,2)]] for N = 2).

We then join this range to get a range of tuples ([(1,1),(1,2),(2,2)] for N = 2).

Next we map to x*y (D's map does for some reason not allow for unpacked tuples, so we need to use indexing).

Penultimately we filter out non-palindromes, before finally reducing the range to its largest element.

BioTronic
  • 2,279
  • 13
  • 15
3

Simple answer, no, D does not have generators or list comprehensions (AFAIK). However, you can create a generator using an InputRange. For that solution, see this related question: What is a "yield return" equivalent in the D programming language?

However, your code isn't using generators, so your code could be translated as:

import std.algorithm : max, reduce, retro, equal;
import std.conv : to;
immutable N = 13;

void main() {
    int[] keep;
    foreach(x; 0 .. N) {
        foreach(y; x .. N) {
            auto val = x*y;
            auto s = to!string(val);
            if (equal(s, s.retro)) // reverse doesn't work on immutable Ranges
                keep ~= val; // don't use ~ if N gets large, use appender instead
        }
    }
    reduce!max(keep); // returns 121 (11*11)
}

For me, this is much more readable than your list comprehension because the list comprehension has gotten quite large.

There may be a better solution out there, but this is how I'd implement it. An added bonus is you get to see std.algorithm in all its glory.

However, for this particular piece of code, I wouldn't use the array to save on memory and instead store only the best value to save on memory. Something like this:

import std.algorithm : retro, equal;
import std.conv : to;
immutable N = 13;

void main() {
    int best = 0;
    foreach(x; 0 .. N) {
        foreach(y; x .. N) {
            auto val = x*y;
            auto s = to!string(val);
            if (equal(s, s.retro))
                best = val;
        }
    }
}
Community
  • 1
  • 1
beatgammit
  • 19,817
  • 19
  • 86
  • 129
  • *However, your code isn't using generators* My code is using a generator expression, note `()` instead of `[]` required for list comprehension. – Paul Jurczak Jul 07 '13 at 19:56
  • @PaulJurczak - I wasn't aware of the difference, so I stand corrected. The more verbose InputRange option (from linked question) would be a more exact translation of your Python code. If I get time I'll look into providing that as an option, since that's the more general way to solve the problem, but the nested loop makes it slightly more interesting to implement. – beatgammit Jul 08 '13 at 06:37