libminizinc icon indicating copy to clipboard operation
libminizinc copied to clipboard

Feature request: ability to generate more than one value in array comprehensions

Open zayenz opened this issue 4 years ago • 4 comments

Array comprehensions are great, and for most 1-D arrays they work very well. However, sometimes I would like to be able to generate more than one element in the expression part in the array comprehension. This is especially useful when generating 2-d matrices for table constraints and for specifying DFAs.

My proposal would be that the current syntax

<array-comp> ::= "[" <expr> "|" <comp-tail> "]"

is replaced with

<array-comp> ::= "[" <expr> "," ... "|" <comp-tail> "]"

As an example, consider generating a mod 13 table for the black-hole patience game, where we want to have a table mapping the values 1..52 to their value mod 13 (as is done in one variant of the model). This case can be handled using element and a 1-d array since it is a functional transformation from a compact range, but is useful for demonstration purposes. With the current syntax, the user is forced to use variants like

array[1..52, 1..2] of int: modtable = array2d(1..52, 1..2, [
        if id_or_value == 1 then id else id mod 13 endif
        | id in 1..52, id_or_value in 1..2];

with the proposed change, this would instead become

array[1..52, 1..2] of int: modtable = array2d(1..52, 1..2, [
        id, id mod 13
        | id in 1..52, _ in 1..2];

which is much simpler to read and understand.

zayenz avatar Aug 12 '20 14:08 zayenz

Btw, you already can do this, which is similar to your proposal.

array[1..52, 1..2] of int: modtable = array2d(1..52, 1..2, [
        [id, id mod 13][i]
        | id in 1..52, i in 1..2]);

schutta avatar Aug 12 '20 23:08 schutta

Thanks, that really is a cleaner way to do these kinds of constructions, although I would argue not the most discoverable :-)

zayenz avatar Aug 13 '20 05:08 zayenz

Note that array comprehensions are one-dimensional arrays and there is no need to have _ in 1..2 in your example. Actually, the way you specify it will create an array with 208 elements instead of 104, because two elements are created for each successful generator call. Thus, the array comprehension should be [id, id mod 13 | id in 1..52] for your example.

I support your feature request for creating n-elements for each successful generator call in array and set comprehension, because it is more intuitive and convenient. But only with the restriction that the same number of elements are created in each call. In that case, MiniZinc might only identify to the number of the elements to be created in a successful generator call and then convert to a comprehensions that it can already handle as given in my example. This means [i, 2*i, 3*i, 4*i | i in 1..10] to [[i, 2*i, 3*i, 4*i][j] | i in 1..10, j in 1..4].

schutta avatar Aug 14 '20 00:08 schutta

Of course, the _ in 1..2 should have been left out! 👍 The trouble of writing code that is not runnable yet.

I agree that it should only be the same number of elements each call, but syntax-wise, that would be a given.

The proposed way of "just" re-writing it to the nested array seems like and efficient way to implement it.

zayenz avatar Aug 14 '20 05:08 zayenz