Merge pull request #110787 from tfc/cartesian-product

lib/attrsets: add cartesianProductOfSets function
This commit is contained in:
Silvan Mosberger 2021-01-29 20:22:12 +01:00 committed by GitHub
commit aa48e205a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 12 deletions

View file

@ -1711,4 +1711,43 @@ recursiveUpdate
</example> </example>
</section> </section>
<section xml:id="function-library-lib.attrsets.cartesianProductOfSets">
<title><function>lib.attrsets.cartesianProductOfSets</function></title>
<subtitle><literal>cartesianProductOfSets :: AttrSet -> [ AttrSet ]</literal>
</subtitle>
<xi:include href="./locations.xml" xpointer="lib.attrsets.cartesianProductOfSets" />
<para>
Return the cartesian product of attribute set value combinations.
</para>
<variablelist>
<varlistentry>
<term>
<varname>set</varname>
</term>
<listitem>
<para>
An attribute set with attributes that carry lists of values.
</para>
</listitem>
</varlistentry>
</variablelist>
<example xml:id="function-library-lib.attrsets.cartesianProductOfSets-example">
<title>Creating the cartesian product of a list of attribute values</title>
<programlisting><![CDATA[
cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
=> [
{ a = 1; b = 10; }
{ a = 1; b = 20; }
{ a = 2; b = 10; }
{ a = 2; b = 20; }
]
]]></programlisting>
</example>
</section>
</section> </section>

View file

@ -183,6 +183,24 @@ rec {
else else
[]; [];
/* Return the cartesian product of attribute set value combinations.
Example:
cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
=> [
{ a = 1; b = 10; }
{ a = 1; b = 20; }
{ a = 2; b = 10; }
{ a = 2; b = 20; }
]
*/
cartesianProductOfSets = attrsOfLists:
lib.foldl' (listOfAttrs: attrName:
concatMap (attrs:
map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
) listOfAttrs
) [{}] (attrNames attrsOfLists);
/* Utility function that creates a {name, value} pair as expected by /* Utility function that creates a {name, value} pair as expected by
builtins.listToAttrs. builtins.listToAttrs.
@ -493,5 +511,4 @@ rec {
zipWithNames = zipAttrsWithNames; zipWithNames = zipAttrsWithNames;
zip = builtins.trace zip = builtins.trace
"lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
} }

View file

@ -78,7 +78,7 @@ let
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
recursiveUpdate matchAttrs overrideExisting getOutput getBin recursiveUpdate matchAttrs overrideExisting getOutput getBin
getLib getDev getMan chooseDevOutputs zipWithNames zip getLib getDev getMan chooseDevOutputs zipWithNames zip
recurseIntoAttrs dontRecurseIntoAttrs; recurseIntoAttrs dontRecurseIntoAttrs cartesianProductOfSets;
inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1 inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1
concatMap flatten remove findSingle findFirst any all count concatMap flatten remove findSingle findFirst any all count
optional optionals toList range partition zipListsWith zipLists optional optionals toList range partition zipListsWith zipLists

View file

@ -629,7 +629,9 @@ rec {
crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]] crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
=> [ "13" "14" "23" "24" ] => [ "13" "14" "23" "24" ]
*/ */
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f]; crossLists = builtins.trace
"lib.crossLists is deprecated, use lib.cartesianProductOfSets instead"
(f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
/* Remove duplicate elements from the list. O(n^2) complexity. /* Remove duplicate elements from the list. O(n^2) complexity.

View file

@ -660,4 +660,71 @@ runTests {
expected = [ [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ]; expected = [ [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
}; };
testCartesianProductOfEmptySet = {
expr = cartesianProductOfSets {};
expected = [ {} ];
};
testCartesianProductOfOneSet = {
expr = cartesianProductOfSets { a = [ 1 2 3 ]; };
expected = [ { a = 1; } { a = 2; } { a = 3; } ];
};
testCartesianProductOfTwoSets = {
expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; };
expected = [
{ a = 1; b = 10; }
{ a = 1; b = 20; }
];
};
testCartesianProductOfTwoSetsWithOneEmpty = {
expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; };
expected = [ ];
};
testCartesianProductOfThreeSets = {
expr = cartesianProductOfSets {
a = [ 1 2 3 ];
b = [ 10 20 30 ];
c = [ 100 200 300 ];
};
expected = [
{ a = 1; b = 10; c = 100; }
{ a = 1; b = 10; c = 200; }
{ a = 1; b = 10; c = 300; }
{ a = 1; b = 20; c = 100; }
{ a = 1; b = 20; c = 200; }
{ a = 1; b = 20; c = 300; }
{ a = 1; b = 30; c = 100; }
{ a = 1; b = 30; c = 200; }
{ a = 1; b = 30; c = 300; }
{ a = 2; b = 10; c = 100; }
{ a = 2; b = 10; c = 200; }
{ a = 2; b = 10; c = 300; }
{ a = 2; b = 20; c = 100; }
{ a = 2; b = 20; c = 200; }
{ a = 2; b = 20; c = 300; }
{ a = 2; b = 30; c = 100; }
{ a = 2; b = 30; c = 200; }
{ a = 2; b = 30; c = 300; }
{ a = 3; b = 10; c = 100; }
{ a = 3; b = 10; c = 200; }
{ a = 3; b = 10; c = 300; }
{ a = 3; b = 20; c = 100; }
{ a = 3; b = 20; c = 200; }
{ a = 3; b = 20; c = 300; }
{ a = 3; b = 30; c = 100; }
{ a = 3; b = 30; c = 200; }
{ a = 3; b = 30; c = 300; }
];
};
} }

View file

@ -444,8 +444,8 @@ in
in in
# We will generate every possible pair of WM and DM. # We will generate every possible pair of WM and DM.
concatLists ( concatLists (
crossLists builtins.map
(dm: wm: let ({dm, wm}: let
sessionName = "${dm.name}${optionalString (wm.name != "none") ("+" + wm.name)}"; sessionName = "${dm.name}${optionalString (wm.name != "none") ("+" + wm.name)}";
script = xsession dm wm; script = xsession dm wm;
desktopNames = if dm ? desktopNames desktopNames = if dm ? desktopNames
@ -472,7 +472,7 @@ in
providedSessions = [ sessionName ]; providedSessions = [ sessionName ];
}) })
) )
[dms wms] (cartesianProductOfSets { dm = dms; wm = wms; })
); );
# Make xsessions and wayland sessions available in XDG_DATA_DIRS # Make xsessions and wayland sessions available in XDG_DATA_DIRS

View file

@ -5,7 +5,11 @@
let let
inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest; inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: { testCombinations = pkgs.lib.cartesianProductOfSets {
predictable = [true false];
withNetworkd = [true false];
};
in pkgs.lib.listToAttrs (builtins.map ({ predictable, withNetworkd }: {
name = pkgs.lib.optionalString (!predictable) "un" + "predictable" name = pkgs.lib.optionalString (!predictable) "un" + "predictable"
+ pkgs.lib.optionalString withNetworkd "Networkd"; + pkgs.lib.optionalString withNetworkd "Networkd";
value = makeTest { value = makeTest {
@ -30,4 +34,4 @@ in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: {
machine.${if predictable then "fail" else "succeed"}("ip link show eth0") machine.${if predictable then "fail" else "succeed"}("ip link show eth0")
''; '';
}; };
}) [[true false] [true false]]) }) testCombinations)

View file

@ -50,10 +50,11 @@ in stdenv.mkDerivation {
homepage = "https://github.com/solo5/solo5"; homepage = "https://github.com/solo5/solo5";
license = licenses.isc; license = licenses.isc;
maintainers = [ maintainers.ehmry ]; maintainers = [ maintainers.ehmry ];
platforms = lib.crossLists (arch: os: "${arch}-${os}") [ platforms = builtins.map ({arch, os}: "${arch}-${os}")
[ "aarch64" "x86_64" ] (cartesianProductOfSets {
[ "freebsd" "genode" "linux" "openbsd" ] arch = [ "aarch64" "x86_64" ];
]; os = [ "freebsd" "genode" "linux" "openbsd" ];
});
}; };
} }