给出一个MiniZinc字符串数组:
int: numStats;
set of int: Stats = 1..numStats;
array[Stats] of string: statNames;
...从MiniZinc数据文件加载数据:
numStats = 3;
statNames = ["HEALTH", "ARMOR", "MANA"];
如何查找数组中特定字符串的索引?例如,ARMOR位于第2位。
我需要针对其统计数据的某些限制找到最佳项目选择。此信息存储在声明如下的2D数组中:
int: numItems;
set of int: Items = 1..numItems;
array[Items, Stats] of float: itemStats;
因此,为了在通过所选项获得的最小ARMOR数量上写一个约束,我需要知道ARMOR在内部数组中有索引2。
由于数据文件是由外部程序生成的,并且统计数据的数量和顺序是动态的,我无法对约束中的索引进行硬编码。
MiniZinc tutorial使用了一个有趣的技巧来实现类似的东西:
set of int: Colors = 1..3;
int: red = 1;
int: yellow = 2;
int: blue = 3;
array[Colors] of string: name = ["red", "yellow", "blue"];
var Colors: x;
constraint x != red;
output [ name[fix(x)] ];
不幸的是,由于MiniZinc数据文件中不允许使用变量声明,因此这个技巧在我的情况下不起作用。
答案 0 :(得分:3)
您可以编写自己的自定义函数来获取字符串数组中字符串的索引:
function int: getIndexOfString(string: str,
array[int] of string: string_array) =
sum( [ if str = string_array[i]
then i
else 0 endif
| i in index_set(string_array) ]
);
在这个函数中,我创建了一个整数数组,其中位置i
的整数等于str
的索引,如果string_array[i]=str
则为0
。例如,对于示例字符串数组["HEALTH", "ARMOR", "MANA"]
和str ARMOR
,生成的int数组将为[0,2,0]
。
这就是我可以简单地对int数组求和以获取字符串索引的原因。如果没有出现字符串,则返回值为0
,这很好,因为MiniZinc中的索引默认以1开头。
以下是您为第一个示例调用上述函数的方法:
int: numStats;
set of int: Stats = 1..numStats;
array[Stats] of string: statNames;
numStats = 3;
statNames = ["HEALTH", "ARMOR", "MANA"];
var int: indexOfArmor;
constraint
indexOfArmor = getIndexOfString("ARMOR",statNames);
solve satisfy;
但请注意,上述功能有限且存在一些缺陷。首先,如果数组中出现多次字符串,则会收到无效索引(发生str
的所有索引的总和)。此外,如果您为字符串数组设置了自己的索引(例如(2..6)
),则需要调整该函数。
答案 1 :(得分:1)
根据Stackoverflow上的this其他帖子,没有办法在MiniZinc中将字符串转换为整数,只是相反。您需要先用其他语言预处理数据并将其转换为整数。但是,一旦完成MiniZinc,你就可以将这些整数转换为字符串。
但是,如果您愿意,可以加载MiniZinc文件而不是数据文件。使用include语法包含任何.mzn文件。
答案 2 :(得分:1)
Andrea Rendl-Pitrey提出的替代方法如下:
array[int] of string: statNames = array1d(10..12, ["HEALTH", "ARMOR", "MANA"]);
var int: indexOfArmor =
sum([i | i in index_set(statNames) where statNames[i] = "ARMOR"]);
solve satisfy;
output [
"indexOfArmor=", show(indexOfArmor), "\n",
];
输出:
~$ mzn2fzn example.mzn ; flatzinc example.fzn
indexOfArmor = 11;
----------
注意: var
可以从indexOfArmor
的声明中删除,因为索引可以静态计算。我把它留在这里仅用于输出目的。
更好的解决方案是声明新的predicate
:
predicate index_of_str_in_array(var int: idx,
string: str,
array[int] of string: arr) =
assert(
not exists(i in index_set(arr), j in index_set(arr))
(i != j /\ arr[i] = str /\ arr[j] = str),
"input string occurs at multiple locations",
assert(
exists(i in index_set(arr))
(arr[i] = str),
"input string does not occur in the input array",
exists(i in index_set(arr))
(arr[i] = str /\ i = idx)
));
强制执行以下两个条件:
str
在arr
str
在arr
<强> e.g 强>
predicate index_of_str_in_array(var int: idx,
string: str,
array[int] of string: arr) =
...
array[10..13] of string: statNames =
array1d(10..13, ["HEALTH", "ARMOR", "MANA", "ATTACK"]);
var int: indexOfArmor;
constraint index_of_str_in_array(indexOfArmor, "ARMOR", statNames);
solve satisfy;
output [
"indexOfArmor=", show(indexOfArmor), "\n",
];
输出
~$ mzn2fzn example.mzn ; flatzinc example.fzn
indexOfArmor = 11;
----------
如果按以下方式更改statNames
array[10..13] of string: statNames =
array1d(10..13, ["HEALTH", "ARMOR", "MANA", "ARMOR"]);
然后mzn2fzn
检测到断言违规行为:
~$ mzn2fzn example.mzn ; flatzinc example.fzn
MiniZinc: evaluation error:
example.mzn:24:
in call 'index_of_str_in_array'
example.mzn:4:
in call 'assert'
Assertion failed: input string occurs at multiple locations
flatzinc:
example.fzn: cannot open input file: No such file
通过搜索数组中没有出现的字符串的索引,可以获得类似的结果。如果没有必要,这种情况当然可以删除。
免责声明: mzn2fzn
的旧版本似乎无法检查index-set
变量的声明array of strings
是否与index-set
的{{1}}匹配正在分配给它的array of strings literal
。此规则在较新版本上强制执行,因为它对其他数据类型也有效。
答案 3 :(得分:0)
另一个更简洁的选择是编写一个使用递归帮助器函数的函数:
% main function
function int: index_of(string: elem, array[int] of string: elements) =
let {
int: index = length(elements);
} in % calls the helper function with the last index
get_index(elem, elements, index)
;
% recursive helper function
function int: get_index(string: elem, array[int] of string: elements, int: index) =
if index == 0
then -1 % the element was not found (base case of recursion)
elseif elements[index] == elem
then index % the element was found
else
get_index(elem, elements, index - 1) % continue searching
endif
;
helper函数从最后一个元素开始,对数组进行递归迭代,并在找到该元素时返回索引。如果在数组中未找到该元素,则返回-1
。另外,您也可以按照Patrick Trentin的建议抛出断言,方法是将then -1
替换为then assert(false, "unknown element: " + elem)
。
调用此函数的示例:
set of int: Customers = 1..5;
array[Customers] of string: ids = ["a-1", "a-2", "a-3", "a-4", "a-5"];
var int: index = index_of("a-3", ids);
var int: unknown_index = index_of("x-3", ids);
其中index
将被分配3
,而unknown_index
将是-1
。