2014-01-17 46 views
1

试想一下,我们有一个被赋予了一个Excel电子表格有三列,标记COND,X和Y条件依赖通行证

COND = TRUE or FALSE (user input) 
X = if(COND == TRUE) then 0 else Y 
Y = if(COND == TRUE) then X else 1; 

这些公式计算在Excel中完全正常,而Excel不生成循环依赖项错误。

我正在写一个编译器,试图将这些Excel公式转换为C代码。在我的编译器中,这些公式确实会产生循环依赖性错误。问题是(天真地)X的表达式取决于Y,Y的表达式取决于X,我的编译器无法在逻辑上继续。

Excel能够完成这项壮举,因为它是一种懒惰的解释型语言。 Excel只会在运行时(用户输入)懒惰地评估公式,并且由于在运行时不会出现循环依赖关系,所以Excel在评估此类逻辑时没有问题。

不幸的是,我需要将这些公式转换为编译语言(不是解释的语言)。实际的电子表格中的实际公式在多个单元格/变量(涉及多达六个不同的单元格)之间具有更复杂的依赖关系。这意味着我的编译器必须对公式进行某种复杂的静态,语义分析,并且足够聪明,以便在“查看”条件分支时检测到没有循环引用。那么编译器将有机会从上面的Excel公式如下C代码:

bool COND; 
int X, Y; 
if(COND) { X = 0; Y = X; } else { Y = 1; X = Y; } 

注意的分配指令的顺序是在C.

我的if语句的每个分支不同问题是,在编译器中是否有任何已建立的算法或文献可以解释如何在编译器中实现这种类型的分析?功能性编程语言编译器是否必须解决这个问题?

回答

1

为什么标准优化技术不够用?

大概,Excel公式形成一个DAG,叶子是原始值,节点是计算/赋值。 (如果Excel计算形成一个循环,那么假设你想要一个固定点,那么你需要某种迭代求解器 )。

如果您只是通过提升它(一个类编译器优化)来传播条件,我们将从您的原始方程开始,其中每个计算都以任意顺序WRT评估给其他人,以便结果计算类似dag(即“ anyorder”是操作者打算模型):

X = if(COND == TRUE) then 0 else Y; 
    anyorder 
Y = if(COND == TRUE) then X else 1; 

然后提起条件:

if (COND) { X=0; } else { X = 1; } 
     anyorder 
if (COND) { Y=X; } else { Y = 1; } 

然后

if (COND) { X=0; anyorder Y=X; } else { X = Y; anyorder Y = 1; } 

每个武器必须是类似dag的。 第一只手臂非常喜欢先评估X = 0分配。 第二只手臂很喜欢先评估Y = 1。所以,我们得到你想要的答案:大约anyorder-IF-daglike知识 似乎给正确的效果

if (COND) { X=0; Y=X; } else { Y = 1; X = Y; } 

所以传统的变革和知识。

我不确定如果将COND作为单元格的函数进行计算,您会做什么。

我怀疑要做到这一点的方法是生成一个计算的依赖关系图与 与依赖关系的条件。您可能必须在弧上传播/分组这些条件,而不像我在语法上做的那样。

+0

我认为“通过提升它来传播条件(一个类编译器优化)”是我正在寻找的。任何参考文献都会有所帮助。你确实描述了我正在尝试做的算法,所以我确实接受了答案。 – n00b101

+0

这个“提升”实际上是一种分配规律:a * b + a * c ==> a *(b + c),使用编程语言的运算符。 –

1

是的,文学存在,对不起,我不能引用任何,我根本不记得和它只是谷歌了,就像你可以..

的依赖和周期分析的基本交易算法是非常简单的。即检测的符号在表达式,建立在形式一组表达式和依赖关系:

inps    expr  outs 
cell_A6, cell_B7 -> expr3 -> cell_A7 
cell_A1, cell_B4 -> expr1 -> cell_A5 
cell_A1, cell_A5 -> expr2 -> cell_A6 

,然后通过比较和迭代地扩大/更换套的输入/输出:

step0: 
cell_A6, cell_B7 -> expr3 -> cell_A7 
cell_A1, cell_B4 -> expr1 -> cell_A5 <--1 note that cell_A5 ~ (A1,B4) 
cell_A1, cell_A5 -> expr2 -> cell_A6  <--1 apply that knowledge here 

so dependency 
cell_A1, cell_A5 -> expr2 -> cell_A6 
morphs into 
cell_A1, cell_B4 -> expr2 -> cell_A6 <--2 note that cell_A6 ~ (A1,B4) and so on 

最后,将得到无论是一套完整的依存关系,在那里你可以很容易地检测到循环依赖,就像例如:

cell_A1, cell_D6, cell_F7 -> exprN -> cell_D6 

,或者,如果没有找到 - 你将能够确定一个安全,增量执行顺序。

如果表达式包含“返回值”以外的分支或副作用,则可以应用各种转换来将表达式减少/展开为新的表达式,或者将表达式组成上述形式的新表达式组。例如:

B5 = { if(A5 + A3 > 0) A3-1 else A5+1 } 

so 

    inps ...  outs 
A3, A5 -> theExpr -> B5 

the condition can be 'lifted' and form two conditional rules: 

A5 + A3 > 0 : A3 -> reducedexpr "A3-1" -> B5 
A5 + A3 <= 0 : A5 -> reducedexpr "A5-1" -> B5 

但是现在,您的执行/分析还必须在应用规则之前处理条件。举重只是其中一种可能的转变。

但是,你仍然需要更多的东西,至少对它来说是一个'扩展'。你问题的难点在于你的表达式很复杂,有分支,而且你需要包含用户随机输入来解析分支来消除死分支和破坏死亡依赖。

由于关键是消除死亡依赖关系,你必须以某种方式检测死亡分支。条件可以是任意的复杂性,并且用户输入是随机的,所以你不能完全静态地完成它。玩转换后,你仍然需要分析条件并相应地生成代码。为此,您需要为条件结果的所有可能组合以及所有分支和规则组合生成代码,除了一些微不足道的情况外,这些组合是不可行的。随着未知数的增加,叶子的数量可以成倍增长(2^N),这是跨越一定阈值后的巨大膨胀。

当然,在分析基础上BOOLS条件,可以分析,组和消除冲突的条件,像(a & b & !a)..

..但如果你的输入值和条件包括非BOOL数据,如整数或浮点数或字符串,只要想象你的条件是有一个条件,执行一些外部奇怪的统计函数,并检查其结果..忽略'怪异'的部分,并专注于'外部'。如果您遇到一些使用AVG或MAX等复杂函数的表达式,则无法通过静态方式(*)来咀嚼。即使简单的算术也很难分析:(a+b)*(c+d) - 你可以推导出一个事实,即当a + b == 0时可以忽略c+d,但这是一个非常艰巨的任务,可以完全覆盖..

IIRC,做可满足性分析SAT)用于基本操作符的布尔表达式是一个NP难题,并没有提及所有数学的整数或浮点数。计算表达式的结果要比告诉哪些​​值真的取决于要容易得多!因此,由于输入值可能是硬编码(酷)或用户在运行时提供(doh!),因此您的compler很可能无法事先对其进行全面分析。现在将它与标记为(*)的事实相关联,很明显,您可以包括一些静态分析,并尝试在“编译时”消除一些分支,但仍然可能有一些部分必须延迟,直到用户提供实际投入。因此,如果部分分析必须在运行时完成,所有分支消除只是一个可选的优化,我认为你现在应该关注运行时间部分。

在最小的未优化版本中,您生成的程序可以简单地记住所有的excel表达式并等待输入数据。一旦程序运行并给出输入,程序就必须替换表达式中的输入,然后尝试迭代地将它们减少为输出值。

将这样的算法写成命令式语言是完全可能的。实际上,你需要写一次,稍后你可以将它与从单元格公式衍生出来的不同规则合并并完成。程序的运行时部分将是相同的,公式会改变。

然后,您可以展开'编译器'端来尝试帮助,即初步部分分析依赖关系,并尝试重新排列规则,以便稍后以“更好的顺序”检查规则,或者通过预先计算常量或内联一些表达式等等,但正如我所说的,这些都是优化,而不是核心功能。可悲的是,我不能真正告诉你任何有关“功能语言”的严肃内容,但是由于通常他们的运行时间是“非常动态”的,有时他们甚至用符号和变换来执行代码,所以它可以降低复杂性你的'编译器'和'引擎'部分。这里最有价值的资产是活力。所以,即使Ruby会比C做得更好 - 但绝不会像你说的那样是一种“编译”语言。

例如,你可以尝试直接将Excel的规则转化为功能:

def cell_A5 = expr1(cell_A1, cell_B4) 
def cell_A7 = expr3(cell_A6, cell_B7) 
def cell_A6 = expr2(cell_A1, cell_A5) 

写下来作为该计划的一部分,那么当在当用户提供了一些值运行时,你将那些只是重新定义程序的一些部分

cell_B7 = 11.2 // filling up undefined variable 
cell_A1 = 23 // filling up undefined variable 
cell_A5 = 13 // overwriting the function with a value 

这是动态平台的力量,没有什么非常'功能'在这里。动态平台可以很容易地填充/覆盖位。但是,一旦用户提供了一些位,并且一旦程序“被即时纠正”,您会首先调用哪一个函数?

答案有点伤心..你不知道。

如果您的动态语言内置了一些规则引擎,您可以尝试生成规则而不是函数,然后依靠该引擎“填充”可能计算的所有内容。

但是,如果它没有规则引擎,你又回到了一个点..

马后炮:

嗯..对不起,我想我只是写了太多和隐约/健谈。如果您认为这有帮助,请给我留言。否则,我会在几天或一周后删除它。