将不连续的单元格视为用作 UDF 参数的单个范围

Treating noncontiguous cells as a single range used as argument of a UDF

我正在尝试使用两个数据集通过插值法构建收益率曲线:到期前的日历天数范围和另一个利率范围。

我在 VBA 上有一个内插利率的 UDF。它使用用户在 excel 工作表上 select 编辑的两个单元格范围作为参数。

我知道 Excel 要求这些范围由连续的单元格组成。 我想要做的是 select 工作表中的非连续单元格,并将其值用作 UDF 参数的范围。

更具体地说,我有两列数据用作范围。但有时我需要在每一列上跳过一个值,并将剩余的值用作我的 UDF 的范围。

我尝试在我的 UDF 中包含另外两个范围参数,并使用 union 方法将两个范围合并为一个范围,以便在我的代码中使用结果范围。没用。

****编辑

克里斯, 感谢您指出手表和即时 Windows。经过多次尝试,代码终于按我的预期运行,但仅针对 DC_1 和 taxas_1 范围使用单独的循环。 奇怪的是,如果我从循环内部删除 "If k > 1 Then" 语句,它将不起作用。所以我需要保留它,让它什么都不做。

我注意到间接函数不能使用像 (A1:A3,C2:C5) 这样的参数,所以我不能使用 indirect((A1:A3,C2:C5)) 作为参数自定义函数。不过,这是个小问题。

万一有人遇到类似问题 post,这里是我使用的代码。

Public Function Interplin_union(ByVal taxas_1 As Range, ByVal DC_1 As Range, ByVal dias As Integer) As Double
Dim tam1 As Long
Dim taxa1 As Double, taxa2 As Double, alfa As Double, d1 As Double, d2 As Double
Dim k As Long
Dim taxas As Variant
Dim DC As Variant

tam1 = taxas_1.Cells.Count
ReDim taxas(1 To tam1)
ReDim DC(1 To tam1)
Interplin_union = -1
Dim c As Range

    k = 1
For Each c In DC_1
    'taxas(k) = taxas_1(k)
    DC(k) = c
    If k > 1 Then
        'Debug.Print DC(k)
        If DC(k - 1) > DC(k) Then
            Interplin_union = CVErr(xlErrNA)
            Exit Function
        End If
    End If
    k = k + 1
Next

    k = 1
For Each c In taxas_1
    taxas(k) = c
        If k > 1 Then
          'Debug.Print DC(k), taxas(k)
        End If
    k = k + 1
Next


For k = 1 To (tam1 - 1)
    If ((DC(k) < dias) And (DC(k + 1) >= dias)) Then
        taxa1 = taxas(k)
        taxa2 = taxas(k + 1)
        alfa = (taxa2 - taxa1) / (DC(k + 1) - DC(k))
        Interplin_union = taxa1 + (alfa * (dias - DC(k)))
    End If
Next k

If (dias <= DC(1)) Then
    Interplin_union = taxas(1)
    ElseIf dias > DC(tam1) Then
    Interplin_union = taxas(tam1)
End If
End Function

可以 实际上循环遍历不连续范围的单元格。

就是说,许多范围属性在应用于不连续范围时,return 第一个连续子范围的 属性 值。

演示:

Dim cl as Range, SomeDiscontiguousRange as Range
Dim Rpt as String
Set SomeDiscontiguousRange = [A1:A3, C2:C5]
For each cl in SomeDiscontiguousRange
    'Do something with cl, eg
    Rpt = Rpt & "," & cl.Address
Next
Debug.Print Rpt 'returns the 7 cell addresses
Debug.Print SomeDiscontiguousRange.Rows.Count 'returns 3, the rows in the first sub-range

那么,如何将其应用到您的情况中呢?我建议两件事:

  1. 不要为您的 UDF 创建额外的参数,而是使用 Excel 公式联合运算符的强大功能 - 将不连续的范围括在公式中的方括号中。
  2. 如上所示循环不连续的范围以将数据映射到变体数组中。然后循环该数组以应用您的逻辑

演示 1

Function Demo(r as range) as Variant
    Demo = r.Address
End Function

在这样的单元格中使用:=Demo((A1:A3,C2:C5)) 注意双括号,这告诉 Excel 将 (A1:A3,C2:C5) 视为单个参数。

您的代码已重构以应用这些方法(以及一些其他优化)

Public Function Interplin_union(ByVal taxas_1 As Range, ByVal DC_1 As Range, ByVal dias As Integer) As Double
    Dim tam1 As Long
    Dim taxa1 As Double, taxa2 As Double, alfa As Double, d1 As Double, d2 As Double
    Dim k As Long
    Dim taxas As Variant
    Dim DC As Variant

    tam1 = taxas_1.Cells.Count
    ReDim taxas(1 To tam1)
    ReDim DC(1 To tam1)
    Interplin_union = -1
    Dim c As Range

    k = 1
    For Each c In DC_1
        taxas(k) = taxas_1(k)
        DC(k) = c
        If k > 1 Then
            Debug.Print DC(k - 1), DC(k)
            If DC(k - 1) > DC(k) Then
                Interplin_union = CVErr(xlErrNA)
                Exit Function
            End If
        End If
        k = k + 1
    Next

    For k = 1 To (tam1 - 1)
        If ((DC(k) < dias) And (DC(k + 1) >= dias)) Then
            taxa1 = taxas(k)
            taxa2 = taxas(k + 1)
            alfa = (taxa2 - taxa1) / (DC(k + 1) - DC(k))
            Interplin_union = taxa1 + (alfa * (dias - DC(k)))
        End If
    Next k

    If (dias <= DC(1)) Then
        Interplin_union = taxas(1)
        ElseIf dias > DC(tam1) Then
        Interplin_union = taxas(tam1)
    End If
End Function

旁注:

  • 你对数据应用的函数逻辑我不做评论,我没有分析过
  • 要访问不连续范围的每个范围的属性,首先循环遍历区域,然后遍历每个区域的单元格。例如

    Dim Arr as Range, Cl as Range 
    For Each Arr in SomeDiscontiguousRange.Areas
        Debug.Print Arr.Rows.Count
        For Each Cl in Arr.Cells
            Debug.Print Cl.Address
        Next
    Next
    
  • Excels 公式联合运算符是 , 和交集运算符是 </code> (Space).<br> 试试 <code>=demo((C4:D7 C2:C12)) 看看我的意思。 See this - "Under Reference operators" heading

  • 您需要添加一些检查来验证范围形状,以防将意外的东西传递给 UDF