Jump to page: 1 2
Thread overview
Better idea for double list comprehension?
Jan 18, 2014
CJS
Jan 18, 2014
CJS
Jan 18, 2014
Stanislav Blinov
Jan 18, 2014
Stanislav Blinov
Jan 18, 2014
bearophile
Jan 18, 2014
CJS
Jan 18, 2014
bearophile
Jan 18, 2014
bearophile
Jan 19, 2014
CJS
Jan 19, 2014
bearophile
Jan 19, 2014
Stanislav Blinov
Jan 19, 2014
CJS
Jan 19, 2014
Stanislav Blinov
Jan 19, 2014
CJS
January 18, 2014
I'm trying to write a D function that does the same as this Python function:

def cross(A, B):
    "Cross product of elements in A and elements in B."
    return [a+b for a in A for b in B]

where A and B are strings. (So cross("ab","12") is ["a1", "b1", "a2", "b2"]).

It's easy to get something that works the same in D, but I'd like to make it as simple and short as possible. The best I could come up with is


string[] cross(string A, string B){
    string[] grid;
    foreach(t; cartesianProduct(A,B)){
        grid ~= (to!string(p[0]) ~ to!string(p[1]));

January 18, 2014
Hit the wrong key and posted too early. I finished the code sample below. My main question was for something prettier and more concise. I feel like the code below is long and not as pretty in comparison to the Python. Sometimes that's an unavoidable consequence of static typing, but I'm not sure that's the case here.

On Saturday, 18 January 2014 at 05:40:56 UTC, CJS wrote:
> I'm trying to write a D function that does the same as this Python function:
>
> def cross(A, B):
>     "Cross product of elements in A and elements in B."
>     return [a+b for a in A for b in B]
>
> where A and B are strings. (So cross("ab","12") is ["a1", "b1", "a2", "b2"]).
>
> It's easy to get something that works the same in D, but I'd like to make it as simple and short as possible. The best I could come up with is
>
>
> string[] cross(string A, string B){
>     string[] grid;
>     foreach(t; cartesianProduct(A,B)){
>         grid ~= (to!string(p[0]) ~ to!string(p[1]));
      }
      return grid;
}
January 18, 2014
I'd say

import std.algorithm;

auto cross(R1,R2)(R1 a, R2 b) {
	return cartesianProduct(a,b).map!"[a[0]]~[a[1]]"();
}

You can always:

import std.array;

auto strings = array(cross("ab","12"));


Although that won't give you a string[], but in a dchar[][].
January 18, 2014
On Saturday, 18 January 2014 at 07:56:15 UTC, Stanislav Blinov wrote:

> Although that won't give you a string[], but in a dchar[][].

...but that is solvable:

auto strings = array(cross("ab","12").map!"to!string(a)"());

Or maybe even by providing additional overload:

auto cross(alias fun,R1,R2)(R1 a, R2 b) {
  return cross(a,b).map!fun();
}

auto strings = array(cross!"to!string(a)"("ab","12"));
January 18, 2014
CJS:

> I'm trying to write a D function that does the same as this Python function:
>
> def cross(A, B):
>     "Cross product of elements in A and elements in B."
>     return [a+b for a in A for b in B]
>
> where A and B are strings. (So cross("ab","12") is ["a1", "b1", "a2", "b2"]).

One solution:


import std.stdio, std.conv, std.algorithm, std.array;

string[] cross(in string A, in string B) {
    return cartesianProduct(A, B).map!(ab => ab[].text).array;
}

void main() {
    cross("ab", "12").writeln;
}


But note that currently cartesianProduct doesn't return the pairs in a natural order.

cross() should be pure.

Bye,
bearophile
January 18, 2014
>
> import std.stdio, std.conv, std.algorithm, std.array;
>
> string[] cross(in string A, in string B) {
>     return cartesianProduct(A, B).map!(ab => ab[].text).array;
> }
>
> void main() {
>     cross("ab", "12").writeln;
> }
>
>
> But note that currently cartesianProduct doesn't return the pairs in a natural order.
>
> cross() should be pure.

Great. Here's a similar follow-up question. I'm trying to reproduce Peter Novig's Python code for solving Sudoku in D (http://norvig.com/sudoku.html). As a way to understand both his code and D/Phobos better. The entire code chunk I'm working on now is

def cross(A, B):
    "Cross product of elements in A and elements in B."
    return [a+b for a in A for b in B]

digits   = '123456789'
rows     = 'ABCDEFGHI'
cols     = digits
squares  = cross(rows, cols)
unitlist = ([cross(rows, c) for c in cols] +
            [cross(r, cols) for r in rows] +
            [cross(rs, cs) for rs in ('ABC','DEF','GHI') for cs in ('123','456','789')])
units = dict((s, [u for u in unitlist if s in u])
             for s in squares)
peers = dict((s, set(sum(units[s],[]))-set([s]))
             for s in squares)

Unfortunately my current best attemp doesn't compile:

import std.stdio : writeln;
import std.range : chunks, chain;
import std.algorithm;
import std.array;
import std.conv;

auto cross(R1, R2)(in R1 A, in R2 B){
    return cartesianProduct(A,B).map!(ab => ab[].text).array;
}


void main(){
    string letters = "ABCDEFGHI";
    string digits = "123456789";

    auto cols = digits;
    auto rows = letters;

    auto squares = cross(rows, cols);
    string[][] unitlist;
    foreach(c; cols){
        unitlist ~= cross(rows, to!string(c));
    }
    foreach(r; rows){
        unitlist ~= cross(to!string(r), cols);
    }
    foreach(r; chunks(rows, 3)){
        foreach(c; chunks(cols, 3)){
            unitlist ~= cross(to!string(r),to!string(c));
        }
    }

    string[][][string] units;
    string[][string] peers;

    foreach(s; squares){
        units[s] = filter!(x=>any!(y=>(s==y)))(unitlist);
    }

    foreach(s; squares){
        peers[s] = remove(chain(units[s]), s);
    }

}

Up until units and peers are defined it works, but I find the to!string conversions ugly. Because of cross being templated I don't think they should be necessary. Taking the cartesian product of a string with a character seems like a reasonable request. Simialrly, so does taking the cartesian product of a string with a range of characters.

For the associative arrays I'm unsure I have the correct calls, but they looked reasonable. Any suggestions?


January 18, 2014
CJS:

> Unfortunately my current best attemp doesn't compile:

What errors are you seeing?


> auto cross(R1, R2)(in R1 A, in R2 B){

Better to add a space before the open brace.
Also you may want to remove "in" if you want to use cross() on lazy ranges.


>     return cartesianProduct(A,B).map!(ab => ab[].text).array;

And a space after the comma.


>     string letters = "ABCDEFGHI";
>     string digits = "123456789";

And a enum/const/immutable for variables that don't need to change.


>         unitlist ~= cross(rows, to!string(c));

to!string(c)   ===>   c.text


>         units[s] = filter!(x=>any!(y=>(s==y)))(unitlist);

Better to use UFCS here, don't forget spaces around operators:

units[s] = unitlist.filter!(x => any!(y => (s == y)));

And filter returns a lazy range, so perhaps that doesn't work.


> For the associative arrays I'm unsure I have the correct calls, but they looked reasonable. Any suggestions?

Phobos needs a very handy set().

Bye,
bearophile
January 18, 2014
> units[s] = unitlist.filter!(x => any!(y => (s == y)));

And you don't need a pair of ( ):

units[s] = unitlist.filter!(x => any!(y => s == y));

And now you need to feed any with some range.

Bye,
bearophile
January 19, 2014
>
> to!string(c)   ===>   c.text
>

That's more concise but I also think it's more confusing. I assume that to!string is doing the exact same thing, but I was hoping for something to do the appropriate implicit conversations. Especially to a range of length 1, though I can understand that kind of auto-magical conversion would be annoying in a number of important instances.

I changed the code to this:



import std.stdio : writeln;
import std.range : chunks, chain;
import std.algorithm;
import std.array;
import std.conv;

auto cross(R1, R2)(R1 A, R2 B) {
    return cartesianProduct(A, B).map!(ab => ab[].text).array;
}


void main(){
    const string letters = "ABCDEFGHI";
    const string digits = "123456789";

    auto cols = digits;
    auto rows = letters;

    auto squares = cross(rows, cols);
    string[][] unitlist;
    foreach(c; cols){
        unitlist ~= cross(rows, to!string(c));
    }
    foreach(r; rows){
        unitlist ~= cross(to!string(r), cols);
    }
    foreach(r; chunks(rows, 3)){
        foreach(c; chunks(cols, 3)){
            unitlist ~= cross(to!string(r),to!string(c));
        }
    }

    string[][][string] units;
    string[][string] peers;

    foreach(s; squares){
        units[s] = unitlist.filter!(x => any!(y => s==y)); \\line 37
    }

    foreach(s; squares){
        peers[s] = remove(chain(units[s]), s); \\line 41
    }

}


On dmd 2.064.2 (downloaded and installed today) on 64-bit linux I get the following errors:

sudoku.d(37): Error: cannot resolve type for any!((y) => s == y)
/home/cjordan1/dmd2/linux/bin64/../../src/phobos/std/algorithm.d(1381): Error: template instance sudoku.main.__lambda1!(string[]) error instantiating
/home/cjordan1/dmd2/linux/bin64/../../src/phobos/std/algorithm.d(1369):        instantiated from here: FilterResult!(__lambda1, string[][])
sudoku.d(37):        instantiated from here: filter!(string[][])
/home/cjordan1/dmd2/linux/bin64/../../src/phobos/std/algorithm.d(1369): Error: template instance sudoku.main.FilterResult!(__lambda1, string[][]) error instantiating
sudoku.d(37):        instantiated from here: filter!(string[][])
sudoku.d(37): Error: template instance sudoku.main.filter!((x) => any!((y) => s == y)).filter!(string[][]) error instantiating
sudoku.d(41): Error: cannot implicitly convert expression (remove(chain(units[s]), s)) of type string[][] to string[]

 Lines 37 and 41 are noted in the above code.


January 19, 2014
CJS:

>         units[s] = unitlist.filter!(x => any!(y => s==y));

One of your problems is that any that needs some range to work on.

Bye,
bearophile
« First   ‹ Prev
1 2