Office加载项:Excel深度优先搜索
tl;dr我在Excel工作表上执行递归函数时遇到问题。下面我将尽可能详细地介绍我的问题,但我希望能提供一个与我试图解决的特定域/问题无关的简单、有效的异步递归示例,因为这样可能更容易 问题背景 我正在尝试构建一个外接程序(任务窗格),以便在电子表格中线性化多变量分析(即假设分析)和约束求解。我的假设是,电子表格固有的二维网格不会随着变量数量的增加而扩展。我想尝试通过一种集成的查询语言来展示这个功能 因为Excel不允许我使用它的解释器,所以我需要将公式链复制到看不见的单元格中,在那里运行我的计算,然后将结果复制到用户想要的地方 这需要我递归地探索有问题的公式,并复制可能受有问题的变量影响的所有嵌套公式 例如,如果我有以下结构Office加载项:Excel深度优先搜索,excel,typescript,office-addins,Excel,Typescript,Office Addins,tl;dr我在Excel工作表上执行递归函数时遇到问题。下面我将尽可能详细地介绍我的问题,但我希望能提供一个与我试图解决的特定域/问题无关的简单、有效的异步递归示例,因为这样可能更容易 问题背景 我正在尝试构建一个外接程序(任务窗格),以便在电子表格中线性化多变量分析(即假设分析)和约束求解。我的假设是,电子表格固有的二维网格不会随着变量数量的增加而扩展。我想尝试通过一种集成的查询语言来展示这个功能 因为Excel不允许我使用它的解释器,所以我需要将公式链复制到看不见的单元格中,在那里运行我的计
A7 -> IF(A5>0, A4, A3)
A5 -> A1 + A2
A4 -> A1 + 10
A3 -> A2 + 10
A1 -> 10
A2 -> 20
我从目标公式开始,探索公式并将其复制到新位置。当我复制它们时,我构建了一个新的环境,告诉我应该去哪里看。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是深度优先搜索+环境累积,这通常非常简单
实际问题
然而,我很难理解Officejs的并发模型。由于每个递归调用都是异步执行的,因此环境的变化就成了问题(虽然这里没有任何竞争条件,但在执行上述调用时,需要存在来自每个深度的绑定,以允许正确的重写)
不幸的是,当我第一次调用这个函数返回时,很多绑定都丢失了。我尝试用async/await
和Promise
API来解决这个问题,但都没有结果。正确的方法是什么?(我100%确信这是一个并发问题。这不仅有意义,而且这段代码在Google AppScript中也能工作,因为在Google AppScript中,并发模型的限制要大得多)
另外,您可能会问,“如果希望按顺序执行,为什么它是异步的?”->这是因为我需要在目标单元格中加载公式,这需要我同步上下文
附言2:我也读过,但似乎找不到解决的办法
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]]
}
异步函数exploreAndCopy(
上下文:Excel.RequestContext,
当前工作表:Excel.Worksheet,
资料来源:string,
目标:字符串,
行:字符串,
列:编号,
环境:地图){
设rx=currentWorksheet.getRange(目标);
rx.load()
wait context.sync();
设cellFormulas=rx.formulas;
如果(cellFormulas.length>0){
变量f:string=cellFormulas[0][0]
var newFormula=f.replace(来源,“A29”)
//获取引用的单元格
let单元格:字符串[]=newFormula.split(/[^A-Za-z0-9]/)
cells=cells.filter(s=>s.match(/^(?=.[a-zA-Z])(?=.[0-9])/);
//删除多个字母
cells=cells.filter((项,索引)=>cells.indexOf(项)==索引);
for(单元格的var c){
//尝试#1
等待exploreAndCopy(上下文、当前工作表、源、c、行、列+1+单元格。索引(c)、环境)
//尝试#2
exploreAndCopy(上下文、当前工作表、源、c、行、列+1+单元格。索引(c)、环境)。然后(p=>
环境forEach((值:字符串,键:字符串)=>{
newFormula=newFormula.replace(键,值)
})
);//将使环境发生变异
//我也试过了
wait context.sync();
}
}
wait context.sync();
//替换地图上的所有内容
var最终公式=新公式
环境forEach((值:字符串,键:字符串)=>{
finalFormula=finalFormula.replace(键,值);
});
//设置环境
环境集合(目标,行+列)
//凌驾
var targetCell=currentWorksheet.getRange(行+列)
targetCell.formulas=[[finalFormula]]
}
感谢您提供的任何建议使用COM API可能更容易做到这一点,COM API具有用于执行公式字符串的求值方法,可以跟踪先例,并且是同步的。(我多年来一直要求在Office JS中进行求值)。您可以使用VBA(XLAM addin)或使用VSTO或XLDNA通过COM互操作使用.NET语言。使用VBA,您的UI将是一个用户窗体(VBA无法访问任务窗格)@charleswillams谢谢你的推荐。COM似乎需要安装特殊工作负载的visual studio发行版,我似乎找不到。你能给我指出正确的方向吗?谷歌“安装VSTO”或查看Excel DNA