So for one of my actions I need the following combination of input from a user:
A OR (B AND\OR C)
Alternatively:
A OR (B AND C) OR (B OR C)
So the following would be valid combinations of inputs:
A
B
C
B & C
I have tried to implement the following:
action (blah) {
description (blah)
type (blah)
collect {
input-group (inputs) {
requires (OneOf)
collect {
input (a) {
type (A)
max (One)
}
input-group (bAndOrC) {
requires (OneOrMoreOf)
collect {
input (b) {
type (B)
max (One)
}
input (c) {
type (C)
max (One)
}
}
}
}
}
}
output (blah)
}
However this gives me an error "illegal combination of max-one constraint (inputs) and min-required member (bAndOrC)".
This error goes away if I change OneOrMoreOf to ZeroOrMoreOf. However this seems like it would mean the user could provide no inputs and it would be considered valid. Or maybe I am misunderstanding and the OneOf in the outer input-group is "inherited" by the inner input-group such that it is effectively OneOrMoreOf?
The easiest way to do this would be to have 2 actions:
Action1 would accept A as the sole input
Action2 would have an input-group that would require OneOrMoreOf and collect B and C as inputs within the group.
These two actions will correctly address the inputs A alone, B alone, C alone, and B&C.
It is important to train multiple types of utterances in your training file to ensure Bixby has enough context to discern between InputA, InputB, and InputC values.
For example, "Input A is 12" and "A is 12" should both be trained to teach Bixby that the context lies more with "A" rather than anything else.
Action1 model:
action (Action1) {
type (Search)
description (__DESCRIPTION__)
collect {
input (inputA) {
type (InputA)
min (Required) max (One)
}
}
output (GenericOutput)
}
Action2 model:
action (Action2) {
type (Search)
description (__DESCRIPTION__)
collect {
input-group (BAndOrC) {
requires (OneOrMoreOf)
collect {
input (inputB) {
type (InputB)
min (Optional) max (One)
}
input (inputC) {
type (InputC)
min (Optional) max (One)
}
}
}
}
output (GenericOutput)
}
So it turns out that the ZeroOrMoreOf nested within a OneOf effectively turns it into a OneOrMoreOf. In the simulator, if I provided no inputs, it prompted me for an input rather than trying to complete the action with no inputs.
However, another problem I ran into was that, in some cases, Bixby couldn't correctly list valid inputs for the user to choose from if too many were provided. It would do fine if, for example, the input was a1, a2, a3. In this case, it would prompt for the user to select one of the three. It would also do fine if, for example, the input was b1, b2, c1, c2. In this case it would prompt for the user to select for one the the B inputs and one of the C inputs. However if the input was for example a1, b1, c1 it would chose the input to be a1, ignoring b1 and c1 without a prompt. If the input was a1, a2, b1, c1, it would prompt the user to choose between a1 and a2, completely ignoring b1 and c1. To solve this I created a preliminary action that would accept any combination and number of A, B, and C and then output all the possible combination for the user to choose from and then use that as an input to the actual action. So my preliminary action looked like
action (preliminary) {
description (blah)
type (blah)
collect {
input-group (inputs) {
requires (OneOrMoreOf)
collect {
input (a) {
type (A)
max (Many)
}
input (b) {
type (B)
max (Many)
}
input (c) {
type (C)
max (Many)
}
}
}
}
output (PreliminaryOutput)
}
The Javascript for this action would generate a list of possible options for the user to choose from. For example if the input was a1, a2, b1, b2, c1, c2 it would list:
a1
a2
b1 c1
b1 c2
b2 c1
b2 c2
The user would chose one of these which went into the actual action:
action (actual) {
description (blah)
type (blah)
collect {
input (preliminary) {
type (PreliminaryOutput)
min (Required)
max (One)
}
output (RealOutput)
}
Related
I want to return the greater number max(loc.x) in a field scope:
sig Locations{
x: set Int // all x locations visited
xgreater: one Int // greater location
}
fact{all loc:Locations| xgreater = greaterX[loc]} // get greater location
fun greaterX[loc:Locations]:Int{ // get the greater location
max(loc.x) // HOW do I do this?
}
Any ideas? Thank you.
How about making it a predicate
pred greatest [locs: set Location, x: Location] {...}
with a constraint that there is no location in lock larger than the x?
(Note also that I've changed the signature name to Location: as with classes and types, the convention is to use the singular noun.)
tl;dr I am having problems with executing a recursive function on an Excel sheet. I will provide as much detail to my problem below as possible but I would appreciate a simple, working async recursive example unrelated to the specific domain/problem I am trying to solve as that might be easier
Problem Context
I am trying to build an add in (task pane) in order to linearize multi-variable analysis (i.e. what if analysis) and constraint solving in spread sheets. My hypothesis is that the inherently two dimensional grid of spreadsheets doesn't scale well as the number of variables grow. I would like to try to surface this functionality through an integrated query language.
Because Excel doesn't allow me to tap into it's interpreter, I need to copy the chain of formulas to cells that are out of view, run my computations there and then copy the results to where the user wants them.
This requires me to recursively explore the formula in question and copy all the nested formulas that may be effected by the variables in question.
For example if I have the following structure.
A7 -> IF(A5>0, A4, A3)
A5 -> A1 + A2
A4 -> A1 + 10
A3 -> A2 + 10
A1 -> 10
A2 -> 20
I start at the target formula and explore the formulas and copy them to a new location. As I copy them, I build a new environment that tell me where to look. i.e.
IF(A5>0, A4, A3)
-- A5
-- A1
-- A2
copy(A5, copyTo = B5, env(A5 -> B5))
-- A4
-- A1
copy(A4, copyTo = B4, env(A5 -> B5, A4 -> B4))
-- A3
-- A2
copy(A3, copyTo = B3, env(A5-> B5, A4-> B4))
new_formula = replaceCells(A7, env) // this will return IF(B5>0, B4, B3)
copy(A7, copyTo=B7, env)
So tl;dr is it's depth first search + environment accumulation, which is normally very simple to do
Actual Problem
However I am having a difficult time wrapping my head around Officejs's concurrency model. Because each recursive call gets executed asynchronously, mutating the environment becomes problematic (while there aren't any race conditions here, the bindings from each depth needs to be present at the time of execution of the one above to allow for correct rewriting
When my first call to this function returns unfortunately a lot of the bindings are missing. I tried solving this issue with both async/await and the Promise API to no result. What is the proper way of going about this? (I am 100% sure this is a concurrency issue. Not only it makes sense but this code works in Google AppScript, where the concurrency model is much more limited).
P.S. You might ask, "if you want this to execute sequentially, why is it async?" -> It's because I need to load in the formulas in the target cell which requires me to sync the context.
P.S. 2: I have also read Avoid using the context.sync method in loops but can't seem to find a way around it.
async function exploreAndCopy(
context: Excel.RequestContext,
currentWorksheet : Excel.Worksheet,
source: string,
target : string,
row : string,
column : number,
env: Map<string, string>) {
let rx = currentWorksheet.getRange(target);
rx.load()
await context.sync();
let cellFormulas = rx.formulas;
if (cellFormulas.length > 0){
var f : string = cellFormulas[0][0]
var newFormula = f.replace(source, "A29")
//Get the referenced cells
let cells : string[] = newFormula.split(/[^A-Za-z0-9]/)
cells = cells.filter(s => s.match(/^(?=.*[a-zA-Z])(?=.*[0-9])/));
//remove dublicates
cells = cells.filter((item, index) => cells.indexOf(item) === index);
for(var c of cells){
// ATTEMP #1
await exploreAndCopy(context, currentWorksheet, source, c, row, column + 1 + cells.indexOf(c), env)
// ATTEMPT #2
exploreAndCopy(context, currentWorksheet, source, c, row, column + 1 + cells.indexOf(c), env).then( p =>
env.forEach((value: string, key: string) => {
newFormula = newFormula.replace(key, value)
})
); // will mutate the env
// Also tried Promises.all
await context.sync();
}
}
await context.sync();
//replace everything in the map
var finalFormula = newFormula
env.forEach((value: string, key: string) => {
finalFormula = finalFormula.replace(key, value);
});
//set env
env.set(target, row + column)
//override
var targetCell = currentWorksheet.getRange(row + column)
targetCell.formulas = [[finalFormula]]
}
Appreciate any recommendations you might have
I am trying to understand the difference between passing a Closure vs a Comparator to the min function on a collection:
// Example 1: Closure/field/attribute?
Sample min = container.min { it.timespan.start }
// Example 2: Comparator
Sample min2 = container.min(new Comparator<Sample>() {
#Override
int compare(Sample o1, Sample o2) {
return o1.timespan.start <=> o2.timespan.start
}
})
They both return the correct result.
Where:
class Sample {
TimeSpan timespan
static constraints = {
}
}
And:
class TimeSpan {
LocalDate start
LocalDate end
}
In Example 1 I just pass the field timespan.start to min which I guess means that I am passing a Closure (even though its just a field in a class)?
In Example 1 does groovy convert the field timespan.start into a Comparator behind the scenes like the one I create explicitly in Example 2?
The difference is, that those are two different min methods both
taking different arguments. There is one for passing
a closure
and one for the
comparator
(there is a third one using identity and some deprecated ones, but we can ignore that for now).
The first version (Closure with one (implicit argument)) you have to
extract the value from the passed value, you want to make the min
aggregate with. Therefor this versions has some inner working to deal
with comparing the values.
But the docs also state:
If the closure has two parameters it is used like a traditional
Comparator. I.e. it should compare its two parameters for order,
returning a negative integer, zero, or a positive integer when the
first parameter is less than, equal to, or greater than the second
respectively. Otherwise, the Closure is assumed to take a single
parameter and return a Comparable (typically an Integer) which is then
used for further comparison.
So you can use a Closure version also to the same as your second example
(you have to define two params explicitly):
container.min{ a, b -> a <=> b }
And there is also a shorter version of the second example. You can cast
a Closure to an interface with groovy. So this works too:
container.min({ a, b -> a <=> b } as Comparator)
I have an array of docker image tags values in string format. I was to sort the values in numerical and display in descending order.
How can I change the below code to sort in numerical order?
class Sorting {
def static main(args) {
def d=["1.0.25.3306",
"0.7.25.3307",
"40.56.25.3308",
"8.78.25.3309",
"4.12.25.3310",
"6.23.25.3311",
"23.45.25.3312",
"12.89.25.3313",
"7.0.25.3314",
"20.5.25.3315",
"8.67.25.3316",
]
println d.sort()
}
}
You can use sort (or toSorted) methods with a custom comparator. Keep in mind, that sort(Closure comparator) mutates the input list, so you may want to use either:
sort(boolean mutate, Closure closure)
or toSorted(Closure closure)
method that creates a new list and keeps the input list unmodified.
For the comparator, you get every version string, tokenize it by . character, and transform every token to the integer value. Without this part, you will keep comparing strings. Let me show you it by example:
def d = ["1.0.25.3306",
"0.7.25.3307",
"40.56.25.3308",
"8.78.25.3309",
"4.12.25.3310",
"6.23.25.3311",
"23.45.25.3312",
"12.89.25.3313",
"7.0.25.3314",
"20.5.25.3315",
"8.67.25.3316",
]
def result = d.toSorted { a, b ->
def (a1, a2, a3, a4) = a.tokenize(".")*.toInteger()
def (b1, b2, b3, b4) = b.tokenize(".")*.toInteger()
return a1 <=> b1 ?: a2 <=> b2 ?: a3 <=> b3 ?: a4 <=> b4
}
result.each { println it }
In the comparator closure, we take every version string and we tokenize it by the . character. Here we capture the output of tokenize() method into multiple variables using multiple assignment, so each part gets assigned to its own variable. Then we use spread operator *. to call toInteger() on each token, which by default is still a type of String. Once we represented two version strings to that form, we can use compare operator <=> to compare every part of the version. We use Elvis operator ?: to compare the following part if the a and b in the current part has the same numeric value. If you run this example, you will get something like this in the output console.
0.7.25.3307
1.0.25.3306
4.12.25.3310
6.23.25.3311
7.0.25.3314
8.67.25.3316
8.78.25.3309
12.89.25.3313
20.5.25.3315
23.45.25.3312
40.56.25.3308
a) Write an integer function Input as follows:
function Input(s)
The function takes in a String parameter that will be used as part of the input prompt, e.g. "Please enter 1st integer" and "Please enter 2nd integer". It returns an integer value corresponding to the user input.
b) Write a function bigger that takes in 2 integer values first and second. It compares which number is bigger and returns one of the following string values:
"1st number is bigger"
"2nd number is bigger"
"The 2 numbers are equal"
c) In the main part of the program, invoke Input(“1st”) and Input(“2nd”) to get the values of first and second. Invoke bigger and display the string returned.
I encountered some problem with my code as it doesn't seem to work as i do not know how to link the inputs together. This is one of my practical questions.
i tried linking the input together but i am unsure of how to do it because the question is kind of confusing.
i do not know how to describe my problem as i do not quite understand the question requirements
I suspect that you're overthinking this.
According to the description, your main program doesn't need to be more complicated than this:
let a = Input("1st");
let b = Input("2nd");
console.log(bigger(a, b));
Now, you "just" need to implement Input and bigger.
Addendum: putting the code you already have in functions is not very complicated:
function Input(prompt)
{
return parseInt(input.question("Please enter " + prompt + " number:"));
}
function bigger(a, b)
{
if (a > b) {
return "1st number is bigger";
}
else if (a < b) {
return "2nd number is bigger";
}
else {
return "The two numbers are equal";
}
}
Use with the code above.