在递归函数中调用作为参数传递的泛型函数

Call a generic function passed as parameter in recursive function

我的愿望是 运行 通过 AddressOf 给定函数的名称和一个输入参数,例如Function Foo(x as Integer) As Integer。我需要进入递归函数的两个输入是函数名称 _name As String 和某种类型的对象 t _list As t (Integer、Double、List(Of Integer) 等)。目标是使用函数名称处理元素或元素列表,因为我需要多次通过给定函数处理列表,并且我不希望在每个位置复制列表处理代码。我尝试尽最大努力处理此类函数(如下)但并未完全崩溃的方法导致了此错误:

Warning: List.Test operation failed. Overload resolution failed because no Public 'ProcessList' can be called with these arguments: 'Public Shared Function ProcessList(Of t)(_func As Func(Of Object,t), _list As System.Object) As IEnumerable(Of t)': Type argument inference fails for argument matching parameter '_func'.

Iterator Function ProcessList(Of t)(_func As Func(Of Object, t), _list As Object) As IEnumerable(Of t)
    If _list.GetType = GetType(List(Of t)) Then
        Yield _list.SelectMany(Function(l) ProcessList(_func, l))
    Else
        Yield _func(_list)
    End If
End Function

作为参考,我找到了一段 Python 代码,它可以有效地满足我的需要,但我对朝这个方向翻译有点生疏(Python 到 VB.net ),而且我对 VB.net 中的这种类型的编程不太熟悉。 Python 片段是:

def ProcessList(_func, _list):
    return map(lambda x: ProcessList(_func, x) if type(x)==list else _func(x), _list)

如果我需要如何调用这个函数,或者如果我的方法有缺陷,如何重写这个函数,我将不胜感激!

更新:

我根据@djv 的信息重新检查了我是如何调用函数和其他一些东西的,我的方法正在运行。首先,由于我与这些函数交互的方式的性质,我必须公开上面的函数:

Public Shared Function Foo(ByVal _input As Object) As Object
    Return Utilities.ProcessList(AddressOf Bar, _input)
End Function

我现在也收到错误消息:

Warning: List.Test operation failed. Unable to cast object of type 'System.Int32' to type 'System.Collections.Generic.IList`1[System.Int32]'.

此时的问题可能在于我调用 ProcessList 函数的方法,而不是我认为的函数本身。我正在与一个不喜欢自己调用 ProcessList 的 GUI 交互,所以我需要这个中间 "helper" 函数,我显然没有正确使用它。

你总是会得到一个 IEnumerable(Of T) 并且 T 可以是原始的(即 Integer)或原始的列表(即 List(Of Integer))。因此,当您尝试使用列表调用它时,您会得到一个 List(Of List(Of Integer)) 例如。

我们可以通过将 ProcessList 分为两种方法来了解原因。它们之间的区别在于第二个参数的类型是 TIEnumerable(Of T)

Sub Main()
    Dim i As Integer = 1
    Dim li As New List(Of Integer) From {1, 1, 1}
    Dim ri As IEnumerable(Of Integer) = ProcessList(AddressOf foo, i).ToList()
    Dim rli As IEnumerable(Of Integer) = ProcessList(AddressOf foo, li).ToList()

    Dim d As Double = 1.0#
    Dim ld As New List(Of Double) From {1.0#, 1.0#, 1.0#}
    Dim rd As IEnumerable(Of Double) = ProcessList(AddressOf foo, d).ToList()
    Dim rld As IEnumerable(Of Double) = ProcessList(AddressOf foo, ld).ToList()

    Console.ReadLine()
End Sub

Function ProcessList(Of T)(f As Func(Of T, T), p As IEnumerable(Of T)) As IEnumerable(Of T)
    Return p.Select(Function(i) ProcessList(f, i)).SelectMany(Function(i) i)
End Function

Iterator Function ProcessList(Of T)(f As Func(Of T, T), p As T) As IEnumerable(Of T)
    Yield f(p)
End Function

Function foo(param As Integer) As Integer
    Return param + 1
End Function

Function foo(param As Double) As Double
    Return param + 1.0#
End Function

以前,我什至无法点击您的原始代码中执行 SelectMany 的那一行。现在,当调用适当的函数时它会被命中。我还重组了该调用以适应新的函数签名。

两个重载都被调用,基于传递给它们的第二个参数。但是,每个 T(基元或其 IEnumerable)只需要一个 foo 方法。