【问题标题】:Call a generic function passed as parameter in recursive function在递归函数中调用作为参数传递的泛型函数
【发布时间】:2020-02-13 06:30:57
【问题描述】:

我的愿望是通过 AddressOf 使用一个输入参数按名称运行给定函数,例如Function Foo(x as Integer) As Integer。我需要递归函数的两个输入是函数名称_name As String 和某种类型的对象_list As t(整数、双精度、列表(整数)等)。目标是处理具有函数名称的元素或元素列表,因为有多次我需要通过给定函数处理列表,并且我不希望在每个位置复制列表处理代码。我尝试调用这种类型的函数(如下)并没有完全崩溃的最佳方式导致了这个错误:

警告:List.Test 操作失败。重载解析失败,因为无法使用这些参数调用公共“进程列表”: '公共共享函数 ProcessList(Of t)(_func As Func(Of Object,t), _list As System.Object) As IEnumerable(Of t)': 参数匹配参数“_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 代码的 sn-p 可以有效地满足我的需要,但是我对这个方向的翻译(Python 到 VB.net)有点生疏,而且我不太熟悉VB.net 中的这种类型的编程。 Python sn-p 是:

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

我现在也收到错误消息:

警告:List.Test 操作失败。 无法将“System.Int32”类型的对象转换为“System.Collections.Generic.IList`1[System.Int32]”类型。

此时的问题可能在于我调用 ProcessList 函数的方法,而不是我想的函数本身。我正在与一个对自己调用 ProcessList 不满意的 GUI 进行交互,因此我需要这个中间“帮助器”函数,但我显然没有正确使用它。

【问题讨论】:

  • 我不确定您需要 ProcessList 做什么。 Enumerable.Select 不正是您所需要的吗?
  • 我的印象是我需要 ProcessList 来实现函数的递归性,因为理想情况下这可以在任何形式的嵌套列表上复制,并且 source 的每个元素本身都可以是一个列表,其元素需要通过Foo。如果不是这样,我将研究如何使Enumerable.Select 以这种方式发挥作用。
  • @pmackni 您提供的 vb.net 代码使用 AddressOf 工作。您是否只想更改它以便可以将函数名作为字符串传递?
  • @djv 这对于故障排除来说更可取,但如果它按原样工作,我将不得不再看看我是如何称呼它的,以及我在我的测试。
  • 我可以看到传递函数名而不是委托的潜在问题,因为委托将包含函数的返回类型,但函数名不会。您的 ProcessList(Of t)(_func As Func(Of Object, t) 从委托中获取 t 的类型。这将要求您将其称为 ProcessList(Of Integer)("foo1", i),这违背了泛型的目的。

标签: vb.net recursion generic-function


【解决方案1】:

你总是会得到一个IEnumerable(Of T)T 可以是一个基元(即Integer)或基元列表(即List(Of Integer))。因此,当您尝试使用 List 调用它时,例如会得到 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 的行。现在,当调用正确的函数时它会被命中。我还重新调整了该调用以适应新的函数签名。

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

【讨论】:

  • 我已经用一些额外的信息更新了我的操作,但它实际上归结为我对如何调用它有一个明显的问题。由于我与函数本身的交互方式以及获取源信息的位置,我必须将原始函数调用包装在另一个函数中。我目前仍在尝试调用函数的变体,因为我实际上与您调用我的函数的位置相同,.ToList() 和所有内容。
  • @pmackni 我只调用ToList() 立即枚举。恐怕我不太明白您的代码在哪里失败:(
  • 嗯,部分原因是,在我没有意识到的某个时候,Visual Studio 在其所有 IDE 智能中,将 ProcessList 的返回类型从 IEnumerable(Of t) 转换为 IEnumerable(Of List (t))。所以这没有帮助。我通过硬编码一些你正在分配东西的行来帮助确定哪里出错了,因为我的界面没有给我具体的失败行,所以我通过它稍微慢了一点。我已经确定了几乎所有问题,唯一挥之不去的问题是输入列表 {1, 1, 1} 出于某种原因转换为 {{1},{1},{1}} 作为输出。
  • @pmackni C# 编译器存在问题,当泛型类型由委托定义时,该问题会阻止方法调用完全泛型。看来这个问题也存在于 vb.net 中。这是一个引用它的github,也与this question 相关。如果不是这种情况,我们可以更简单地解决您的问题。
  • 当您像foo(p As Object) As List(Of Integer) 一样传递List(Of Integer) 时,根据ProcessList,通用T 是List(Of Integer)。我认为这是你设计的一个缺点。
猜你喜欢
  • 2019-03-05
  • 2016-05-28
  • 2021-09-23
  • 1970-01-01
  • 2018-12-12
  • 2011-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多