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
Related
let mut FEED_CA_:HashMap<(Address, Address, Address), (iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>)> = HashMap::new();
for i in FEED_CA_START{
let c0 = i[0];
let c1 = i[1];
let c2 = i[2];
let p_01_ = ALL_C.get(&&(*c0, *c1)).ok_or(continue).unwrap();
FEED_CA_START is a vec of (c0, c1, c2) tuples :)
ALL_C is a HashMap of <(Address, Address), Address> from which I can't get the value out.
The whole iteration fails on last line in the above snippet. There's eight more of those get()s, and not one of them gets filled, the output data structure (FEED_CA_ ) to which I insert results after iterating through the whole db is always len = 0.
I've:
Printed out the values &(c0, c1) for each i in my tuple FEED_CA_START
Printed out the values of the faulty &(c0, c1), as well as &(*c0, *c1) and even &&(*c0, *c1).
Manually found that they indeed do match, do exist (100% of those calls should succeed).
Printed out the types of the above and made sure they indeed do match. Same result, even if types of the query and key match and are both exactly: &(primitive_types::H160, primitive_types::H160)
Done some borrow&deref trial-and-error combinations, there's only so many I can try, and still no hope.
printed out the databases I've derived those from and manually checked - there's multiple matches, and the program returns exactly 0 always, and I've tried multiple functions/methods/iterations, my program always breaks at get() function, goes straight to continue.
I ran a simple test where in a new program I've created a HashMap with addresses akin to ALL_C , and accessed it using a tuple key - and there it worked. Here it doesn't. I've even thrown some reference/typing spaghetti there to make sure it breaks as well, it didn't. Got the value every time. Here I'm trying as hard as I can to make it work, and it's day 2 stuck on this issue, dead end.
How do I approach a problem like that? I'm lost.
If I've understood the code snippet is correctly, I think your problem is the .ok_or(continue).
Arguments passed to ok_or are eagerly evaluated; if you are passing the result of a function call, it is recommended to use ok_or_else, which is lazily evaluated.
In other words, the continue is always evaluated and so the code is really not doing what you expect.
let p_01_ = ALL_C.get(&(c0, c1)).ok_or(continue).unwrap();
println!("unreachable");
As a possible solution, you could use filter_map which will ignore all None results and then check the length. It doesn't seem very elegant but it might be better that repeated if let blocks:
// Note: I just guessed some types for an example
for i in FEED_CA_START {
let c0 = i.0;
let c1 = i.1;
let c2 = i.2;
let args = vec![(c0, c1), (c0, c2)];
let ps: Vec<&Address> = args.iter().filter_map(|arg| ALL_C.get(arg)).collect();
if ps.len() != args.len() {
continue;
}
// Do something
}
As per the other answer, continue is evaluated so it never get to check the other code. IMO it should be imposible to construct that in the first place.
On the other hand, it is not a good construct to do that, you should use something more rusty like:
let mut FEED_CA_:HashMap<(Address, Address, Address), (iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>)> = HashMap::new();
for i in FEED_CA_START{
let c0 = i[0];
let c1 = i[1];
let c2 = i[2];
if let Some(p_01_) = ALL_C.get(&&(*c0, *c1)) {
// compute stuff over `p_01_`
} else { // else could be avoided if you don't need to compute anything else in the loop if there is nothing for the key
continue
};
...
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
I am new to Macaulay2. I tried to use the following command to show that a quotient ring S=ZZ_977[x]/<7x^11+4x^5-23x^4+x-27> is a field:
i2 : S = ZZ[x]/<7*x^11+4*x^5-23*x^4+x-27>
o2 = S
IsField S
But it doesn't work. I looked it up on internet but they don't cover this part. Can someone help? Thanks!
The command (or method in M2 speak) you're looking for is isField rather than IsField.
i1 : R = ZZ[x];
i2 : I = ideal(7*x^11+4*x^5-23*x^4+x-27);
o2 : Ideal of R
i3 : S = R/I;
i4 : isField S
o4 = false
i5 : isField (ZZ/2)
o5 = true
But there is (or at least was as of February 2014) a major caveat about using this internal M2 method for testing whether a quotient ring is a field (alternately testing the maximality of the ideal).
More generally, the applicable rules from the Macaulay2 style guide for naming methods and variables are
Names representing methods must be verbs and written in mixed case
starting with lower case.
The prefix "is" should be used for Boolean variables and methods.
I have written a little tool in VBA that charts a function you pass it as a string (e.g. "1/(1+x)" or "exp(-x^2)"). I use the built-in Evaluate method to parse the formula. The nub of it is this function, which evaluates a function of some variable at a given value:
Function eval(func As String, variable As String, value As Double) As Double
eval = Evaluate(Replace(func, variable, value))
End Function
This works fine, e.g. eval("x^2, "x", 2) = 4. I apply it element-wise down an array of x values to generate the graph of the function.
Now I want to enable my tool to chart the definite integral of a function. I have created an integrate function which takes an input formula string and uses Evaluate to evaluate it at various points and approximate the integral. My actual integrate function uses the trapezoidal rule, but for simplicity's sake let's suppose it is this:
Function integrate(func As String, variable As String, value As Double) As Double
integrate = value * (eval(func, variable, 0) + eval(func, variable, value)) / 2
End Function
This also works as expected, e.g. integrate("t", "t", 2) = 2 for the area of the triangle under the identity function.
The problem arises when I try to run integrate through the charting routine. When VBA encounters a line like this
eval("integrate(""t"",""t"",x)", "x", 2)
then it will stop with no error warning when Evaluate is called inside the eval function. (The internal quotes have to be doubled up to read the formula properly.) I expect to get the value 2 since Evaluate appears to try and evaluate integrate("t", "t", 2)
I suspect the problem is with second call on Evaluate inside integrate, but I've been going round in circles trying to figure it out. I know Evaluate is finicky and poorly documented http://fastexcel.wordpress.com/2011/11/02/evaluate-functions-and-formulas-fun-how-to-make-excels-evaluate-method-twice-as-fast but can anyone think of a way round this?
Thanks
George
Excel 2010 V14, VBA 7.0
Thanks Chris, your Debug.Print suggestion got me thinking and I narrowed the problem down a bit more. It does seem like Evaluate gets called twice, as this example shows:
Function g() As Variant
Debug.Print "g"
g = 1
End Function
Run from the Immediate Window:
?Evaluate("g()")
g
g
1
I found this http://www.decisionmodels.com/calcsecretsh.htm which shows a way round this by using Worksheet.Evaluate (Evaluate is actually the default for Application.Evaluate):
?ActiveSheet.Evaluate("g()+0")
g
1
However this still doesn't solve the problem with Evaluate calling itself. Define
Function f() As Variant
Debug.Print "f"
f = ActiveSheet.Evaluate("g()+0")
End Function
Then in the Immediate Window:
?ActiveSheet.Evaluate("f()+0")
f
Error 2015
The solution I found was define a different function for the second formula evaluation:
Function eval2(formula As String) As Variant
[A1] = "=" & formula
eval2 = [A1]
End Function
This still uses Excel's internal evaluation mechanism, but via a worksheet cell calculation. Then I get what I want:
?eval2("f()")
f
g
1
It's slower due to the repeated worksheet hits, but that's the best I can do. So in my original example, I use eval to calculate the integral and eval2 to chart it. Still interested if anyone has any other suggestions.
I'm just now starting to dive into IF statements in R. From what I see from the CRAN documentation on IF statements, it looks that all IF statements must be nested.
Is this true? If it is, this IF/THEN structure is more like EXCEL and, I think, not as straight forward as RUBY or Python IF/THEN logic. Am I not interrupting this correct?
In EXCEL (the gui, not VBA), you must run a formula like this:
#IF Statement 1
=IF(A1<20, A1*1,
#IF Statement 2
IF(A1<50, A1*2,
#IF Statement 3
IF(A1<100, A1*3, A1*4)
#Closes IF Statement 2
)
#Closes IF Statement 1
)
Nested IF/THEN are complicated because you have ensure you close the functions properly.
This next part - I'm not 100% sure on, as I am a beginner in both languages, but... In Ruby or Python, you can explicitly write an IF function in a more structured manner:
IF
ELSE
END
This is much simpler and explicit.
Am I missing a proper way to run this in R, or is it that complicated? Is there a good resource that I have not found yet on IF/THEN/Loop for R?
Thanks
There are actually two forms of if-else flow-control logic available in R.
The if statement is, to a first approximation, much like C, C++, or Java's if. Just like in those languages, you can chain ifs in sequence.
if(test) {
statements
}
else if(test2) {
statements
}
else {
statements
}
R also has the ifelse function, which is indeed much like Excel's =IF. The rough equivalent of the if-elseif-else above would be
ifelse(test, result1, ifelse(test2, result2, result3))
A key difference is that in the second example, test, result1, result2 and result3 are all vectors.
You should use the first if you want to do the same set of operations on your entire dataset, but which set depends on a test. The second is meant for vectorised calculations, where you want to carry out different operations on each element of a vector.
Many new users of R are confused about if. It evaluates only a single value and then executes either the expression that follows or the else clause. In R the ifelse function is generally what former SAS, Excel, and SPSS users are going to want and it will support nesting. There is the switch function that might be helpful in some instances, although I do not see how your set of non-exclusive logical conditions would immediately fit into its logic.
In your case, I would think instead about using the findInterval function. This would accomplish the combined operations of logical and mathematical operation in your example (and would return a vector if "A" were a vector) :
A*( 1+ findInterval( A, c(20,50,100) ) ) # OR
A*( 1+ findInterval( A, c(-Inf, 20, 50, 100) ) ) # the equivalent using -Inf
And thinking about it a bit further The findInterval function could also be used as the first argument to switch if you wanted a function to be applied to "A".
(Further comment: I was assuming that your "A1" expression would get copied down a column or row of cellls in an Excel spreadsheet and would in the process have the row or column references incremented in the particular automagical manner that Excel supports becoming A2, A3, etc. That is a different programing perspective than any of the more general languages you are comparing to. Operations on R vectors are analogous but would not generally need the "1", "2", "3" ... entries and so I omitted them from the code.)
I am not sure I understand the question, but a natural R equivalent of your Excel code would be
if (a1 < 20)
a1 * 1
else if (a1 < 50)
a1 * 2
else if (a1 < 100)
a1 * 3
else
a1 * 4
And you could put curly braces around the a1 * n expressions if you wanted. However, if a1 is a vector rather than a scalar, you probably want to evaluate the comparisons in parallel for all vector elements, which is done with ifelse, which does nest like your Excel construct:
ifelse(a1 < 20, a1 * 1,
ifelse(a1 < 50, a1 * 2,
ifelse(a1 < 100, a1 * 3,
a1 * 4)))
A third way to write it, for vector a1, takes advantage of logical indexing:
a2 <- a1 # take a copy
a2[a1 >= 20 & a1 < 50] <- a1[a1 >= 20 & a1 < 50] * 2
a2[a1 >= 50 & a1 < 100] <- a1[a1 >= 50 & a1 < 100] * 3
a2[a1 >= 100 ] <- a1[a1 >= 100 ] * 4