Index of string value in MiniZinc array

落花浮王杯 提交于 2019-12-06 02:56:08

You can write your own custom function to get the index of a string within a string array:

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) ]
   );

In this function I create an array of integers where the integer at position i either equals the index of str if string_array[i]=str and 0 otherwise. For instance, for your sample string array ["HEALTH", "ARMOR", "MANA"] and str ARMOR the resulting int array will be [0,2,0].

This is why I can simply sum over the int array to get the index of the string. If the string does not occur, the return value is 0, which is fine since indices in MiniZinc start with 1 by default.

Here is how you can call the function above for your first example:

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;  

Note however that the function above is limited and has some flaws. First, if you have multiple occurrences of the string in the array, then you will receive an invalid index (the sum of all indices where str occurred). Also, if you have your own index set for your string array (say (2..6)), then you will need to adapt the function.

An alternative approach to that presented by Andrea Rendl-Pitrey, is the following one:

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",
];

which outputs:

~$ mzn2fzn example.mzn ; flatzinc example.fzn
indexOfArmor = 11;
----------

note: that var can be dropped from the declaration of indexOfArmor, since the index can be statically computed. I kept it here only for output purposes.


A better solution is to declare a new 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)
    ));

which enforces both of the following conditions:

  • str occurs at least once in arr
  • str does not occur multiple times in 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",
];

outputs

~$ mzn2fzn example.mzn ; flatzinc example.fzn
indexOfArmor = 11;
----------

If one changes statNames in the following way

array[10..13] of string: statNames =
                 array1d(10..13, ["HEALTH", "ARMOR", "MANA", "ARMOR"]);

then mzn2fzn detects an assertion violation:

~$ 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

A similar result would be obtained by searching for the index of a string that does not occur in the array. This condition can of course be removed if not necessary.


DISCLAIMER: older versions of mzn2fzn don't seem to check that the declared index-set of an array of strings variable matches the index-set of an array of strings literal that is being assigned to it. This rule is enforced on newer versions, as it is also valid for other data types.

Kobbe

According to this other post on Stackoverflow there is no way of converting strings to integers in MiniZinc, only the other way around. You need to first pre process your data in some other language and turn it into integers. You can however turn those integers into string once you are done in MiniZinc.

You can however load MiniZinc files instead of data files if you would like. Use the include syntax to include any .mzn file.

Another, cleaner option is to write a function that uses a recursive helper function:

% 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  
;

The helper function iterates recursively over the array, starting from the last element, and when it finds the element, it returns the index. If the element was not found in the array, then -1 is returned. Alternatively, you can also throw an assertion following the suggestion of Patrick Trentin by replacing then -1 with then assert(false, "unknown element: " + elem).

An example of calling this function:

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);

where index will be assigned 3 and unknown_index will be -1.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!