Excel UDF 将评估的 SUB 的值加倍

Posted

技术标签:

【中文标题】Excel UDF 将评估的 SUB 的值加倍【英文标题】:Excel UDF doubles value of the evaluated SUB 【发布时间】:2017-08-08 09:24:59 【问题描述】:

1. 我试图回答 VBA UDF to split string array,但在计算我的 UDF 时得到了令人不快的结果。

Public Function mytest(src, dest)
    dest.Parent.Evaluate "test(" & src.Address(False, False) & ", " & dest.Address(False, False) & ")"
    mytest = "wut"
End Function

Sub test(src As Range, dest As Range)
    Dim chr, rows, cols
    rows = 0
    cols = 0
    For chr = 1 To Len(src.Value)
        Select Case Mid(src.Value, chr, 1)
            Case ","
            rows = rows + 1
            Case ";"
            cols = cols + 1
            rows = 0
            Case Else
            Cells(dest.Row + rows, dest.Column + cols).Value = Cells(dest.Row + rows, dest.Column + cols).Value & Mid(src.Value, chr, 1) '
        End Select
    Next chr
End Sub

预期结果: 公式结果: 有人可以解释为什么它会使单元格的值加倍吗? 当我使用

调试 test
Sub ffs()
    Call test(Cells(1, 1), Cells(3, 1))
End Sub

我得到了预期的结果,所以我猜问题不在 test Sub?..

2. 每当我尝试向 Function 和 Sub(例如分隔符)添加更多参数时,Function 根本不会评估 Sub

Public Function CellToRange(src, dest, DelimL, DelimC)
    dest.Parent.Evaluate "test(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", " & DelimL & ", " & DelimC & ")"
    CellToRange = "wut"
End Function

Sub CTR(src As Range, dest As Range, Delim1, Delim2)
    Dim chr, rows, cols
    rows = 0
    cols = 0
    For chr = 1 To Len(src.Value)
        Select Case Mid(src.Value, chr, 1)
            Case Delim1
            rows = rows + 1
            Case Delim2
            cols = cols + 1
            rows = 0
            Case Else
            Cells(dest.Row + rows, dest.Column + cols).Value = Cells(dest.Row + rows, dest.Column + cols).Value & Mid(src.Value, chr, 1) '
        End Select
    Next chr
End Sub

请帮助._。并提前致谢。解决方案: 谢谢比利和查尔斯·威廉姆斯。 改变

dest.Parent.Evaluate "CTR(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", " & DelimL & ", " & DelimC & ")"

dest.Parent.Evaluate "0+CTR(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", " & DelimL & ", " & DelimC & ")"

谢谢大家!

【问题讨论】:

Stop VBA Evaluate from calling target function twice的可能重复 @Billy That.. 实际上解决了我的问题 【参考方案1】:

问题在于 Worksheet.Evaluate 方法被用来绕过 UDF 不允许修改工作表结构的限制。

考虑这段代码

Option Explicit

Public Function dummyudf() As String
    Debug.Print "Calling Evaluate method"
    ActiveSheet.Evaluate "testsub()"
    Debug.Print "Returning From Evaluate method"
    dummyudf = "done"
End Function

Sub testsub()
    Debug.Print "testsub running"
End Sub

Sub testmacro()
    Dim s As String
    Debug.Print "testmacro running"
    s = dummyudf
End Sub

UDF dummyudf() 使用 Evaluate 方法将 invoke Sub 称为 testsub()。这些类似于 OP 的第 1 部分中的 mytesttest 以及第 2 部分中的 CellToRangeCTR,但被精简到最低限度。

testsub() 也可以直接作为宏调用。第二个宏 testmacro 调用 dummyudf 作为 VBA 中的函数。

从即时窗口获得以下输出:

可以看出

当作为宏调用时:testsub() 的行为符合预期

dummyudf() 作为工作表上的 UDF 调用时(例如,通过将公式 =dummyudf() 添加到单元格 A1 中,Evaluate 方法似乎调用 testsub() 两次

dummyudf() 作为 VBA 中的函数调用时,通过将 testmacro() 作为宏运行,Evaluate 方法似乎调用了两次 testsub()

here 的文档建议 Worksheet.Evaluate 方法的 Name 参数应该是对象的名称,因此可以提供 @ 的名称有点令人惊讶987654349@。它似乎也调用了任何这样的Sub 两次,这更令人惊讶,但确实强调了 YowE3K 的回答中给出的关于不在 UDF 中使用此 hack 的建议。我会更进一步:不要将Worksheet.Evaluate 与任何Sub 一起使用。

【讨论】:

很抱歉我不得不这样做,好奇心害死猫。【参考方案2】:

1) 它在公式被触发时计算一次,当单元格 A3 被函数更新时再次计算(因为它是公式所依赖的单元格之一)。

2a) 你调用了错误的子程序(test 而不是CTR

2b) 您需要使用类似

的方式调用第二个函数
=CellToRange(A1;A3;""",""";""";""")

或者将代码中调用 CTR 的行更改为

dest.Parent.Evaluate "CTR(" & src.Address(False, False) & ", " & dest.Address(False, False) & ", """ & DelimL & """, """ & DelimC & """)"

3) 我强烈建议您不要使用这种 hack 来获取 UDF 来更新包含该函数的单元格以外的单元格。

【讨论】:

@ YowE3k 1) 函数不应该被调用两次来评估两次吗?当我在函数开始时切换断点并在工作表上计算时,我看到它只运行了 1 次。对不起,如果我理解不正确。 2a) 行动! ^^' 2b) 做到了 3) 我只是为了提高才做到的 @ YowE3K 2a) 嗯,它没有成功 2b) 更改了行,现在它将 src cell 复制到 dest cell (hmmm) 另外,当我计算 CellToRange 时,它的评估结果没有像 my test @AntiDrondert 1) 是的,看来我弄错了。单步执行代码显示执行一次评估,并且在函数完成之前根本不会更新工作表。我尝试偏移它写入的位置,这样它就不会写入引用的单元格,它仍然会复制东西。 2b)这两种方法对我来说都可以(除了重复) 2b) 忘记了另一个双引号(太痛苦了,几乎和 Lisp 中的括号一样糟糕)现在它工作正常......除了重复。也许是有原因的? Evaluate 可以充当“呼叫”Sub 两次吗?我不熟悉这种方法。

以上是关于Excel UDF 将评估的 SUB 的值加倍的主要内容,如果未能解决你的问题,请参考以下文章

Spark SQL - 非确定性 UDF 的单一评估

雪花不支持的子查询类型无法在 UDF 标量中评估

自定义配置单元 UDF 中的覆盖评估方法

自动重新评估非易失性 UDF

VBA UDF 在每次更改后进行评估

如何在 hive udf 中使用 collect_set 的结果 - 评估方法?