当前位置: 首页 > 面试题库 >

递归通用用法

米迪
2023-03-14
问题内容

编辑:“
我从’erickson’那里收到了一个非常相关的答案,但是存在一个附带问题(向上投射?),这个问题在我的原始示例中并未明确涵盖,并且无法用他的答案解决。我将该示例扩展到涵盖了另一个问题,我已在本文结尾处将其包括在内。感谢您的帮助。

我目前面临Java泛型的问题,该问题与所谓的“好奇地重复的通用模式”有关。在阅读了Jon
Skeet对这个问题“ java枚举定义”的答案之后,我以为找到了解决方案。但是,当我尝试将其应用到代码中时,发现自己遇到了其他问题。

我提出了一个“小”示例,其中出现了我所面临的问题。我希望它将足够清楚地说明我的问题。

示例说明: 我想构建一个节点类型可以变化的图形。我定义了一个抽象类 Node ,它定义了一些基本方法,以及一个实现这些方法的具体类,即
ConcreteNode 。我还创建了一个名为 City 的ConcreteNode专业化项目。

在给定图中,一个重要的要求是,所有元素都应由其相同的类型或子类型构成,即,ConcreteNode的图只能具有ConcreteNodes
Cities。

这些是我的课程的定义:

abstract class Node<T extends Node<T>>
class ConcreteNode<T extends ConcreteNode<T>> extends Node<T>
class City extends ConcreteNode<City>

这些定义使用在Enum类的定义中也可以找到的“重复通用模式”:

Class Enum<E extends Enum<E>>

问题: 使用这些类时遇到问题。如果我必须停留在层次结构中的“城市”级别(即将城市连接到城市),我没有问题,但是在尝试访问其他类时遇到 很大的
问题。

在以下代码中,我的问题可以在GraphUtil方法的签名中看到:

  1. addNewNeighbors1a 使用原始类型Node,但至少可以使用。
  2. addNewNeighbors1b 使用Node类型,但是它根本不编译(错误包含在代码中)。
  3. addNewNeighbors1c 为Node使用了一个更复杂的参数,我希望它可以正常工作,但无法编译(错误包含在代码中)。
  4. addNewNeighbors3 对Node使用复杂的参数,但是即使node和newNode的参数相同,也不会再次编译。

综合而言,我的问题是 如何对这些参数化的通用类型进行转换?

假设这些方法将位于对City甚至ConcreteNode一无所知的库中,我将非常高兴获得有关GraphUtil方法的最佳签名的帮助。

谢谢你们。

这是示例的完整代码

package test.city;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class TestCity {
    abstract class Node<T extends Node<T>> {
    public abstract void addNeighbor(T n);
    public abstract void addNeighbors(Collection<? extends T> nodes);
    public abstract Collection<T> neighbors();
    }

    class ConcreteNode<T extends ConcreteNode<T>> extends Node<T> {
    protected Collection<T> _neighbors = new ArrayList<T>();

    @Override
    public void addNeighbor(T n) {
        _neighbors.add(n);
    }

    @Override
    public void addNeighbors(Collection<? extends T> nodes) {
        _neighbors.addAll(nodes);
    }

    @Override
    public Collection<T> neighbors() {
        return _neighbors;
    }
    }

    class City extends ConcreteNode<City> {
    protected String _name;

    public City(String name) {
        _name = name;
    }

    @Override
    public String toString() {
        return _name;
    }
    }

    public TestCity() {
    City nyc = new City("NYC");
    nyc.addNeighbor(new City("Boston"));
    nyc.addNeighbor(new City("Wash"));

    GraphUtil.print("Printing cities", nyc.neighbors());

    GraphUtil.printNeighbors1(nyc);
    GraphUtil.printNeighbors2(nyc);
    GraphUtil.printNeighbors3(nyc);
    GraphUtil.printNeighbors4(nyc);
    GraphUtil.addNewNeighbors1a(nyc, new City("Miami"));
    GraphUtil.addNewNeighbors2(nyc, new City("NewOr"));
    GraphUtil.addNewNeighbors3(nyc, new City("Dallas"));
    }

    static class GraphUtil {
    static void printNeighbors1(Node<?> node) {
        print("Nodes", node.neighbors());
    }

    static void printNeighbors2(ConcreteNode<?> node) {
        print("Concrete nodes", node.neighbors());
    }

    static void printNeighbors3(Node<? extends Node<?>> node) {
        print("Nodes2", node.neighbors());
    }

    static void printNeighbors4(ConcreteNode<? extends ConcreteNode<?>> node) {
        print("Concrete nodes2", node.neighbors());
    }

    static void addNewNeighbors1a(Node node, City newNode) {
        node.addNeighbor(newNode);
        print("Add city to node", node.neighbors());
    }

    static void addNewNeighbors1b(Node<?> node, City newNode) {
        // node.addNeighbor( newNode ); <---- DOES NOT COMPILE!!!
        // The method addNeighbor(capture#8-of ?) in the type
        // TestCity.Node<capture#8-of ?>
        // is not applicable for the arguments (TestCity.City)
    }

    static void addNewNeighbors1c(Node<? extends Node<?>> node, City newNode) {
        // node.addNeighbor( newNode ); <---- DOES NOT COMPILE!!!
        // The method addNeighbor(capture#9-of ? extends TestCity.Node<?>)
        // in the type
        // TestCity.Node<capture#9-of ? extends TestCity.Node<?>> is not
        // applicable for the arguments (TestCity.City)

    }

    static void addNewNeighbors2(Node node, ConcreteNode newNode) {
        node.addNeighbor(newNode);
        print("Add concrete node to node", node.neighbors());
    }

    static void addNewNeighbors3(Node<? extends Node<?>> node,
        Node<? extends Node<?>> newNode) {
        // node.addNeighbor( newNode ); <---- DOES NOT COMPILE!!!
        // The method addNeighbor(capture#8-of ? extends TestCity.Node<?>)
        // in the type
        // TestCity.Node<capture#8-of ? extends TestCity.Node<?>> is not
        // applicable for the arguments
        // (TestCity.Node<capture#10-of ? extends TestCity.Node<?>>)
    }

    static void print(String msg, Collection<?> col) {
        System.out.println(msg + ": " + Arrays.toString(col.toArray()));
    }
    }

    public static void main(String[] args) {
    new TestCity();
    }

}

运行此代码的输出如下(一点都不奇怪):

Printing cities: [Boston, Wash]
Nodes: [Boston, Wash]
Concrete nodes: [Boston, Wash]
Nodes2: [Boston, Wash]
Concrete nodes2: [Boston, Wash]
Add city to node: [Boston, Wash, Miami]
Add concrete node to node: [Boston, Wash, Miami, NewOr]

问题的第二部分

我认为原始解决方案中也存在一个相关问题,因为我认为该解决方案也将适用。

现在,我向GraphUtil添加了以下方法:

static <T extends Node<T>> T getSomeNeighbor(T node) {
    return node.neighbors().iterator().next();
}

从我的主班我正在尝试以下方法:

City someCity = GraphUtil.getSomeNeighbor(nyc); 
someCity.addNeighbor(new City("London")); // OK

ConcreteNode someCN1 = GraphUtil.getSomeNeighbor(nyc); 
someCN1.addNeighbor(new City("Paris")); // OK, but raw

ConcreteNode<?> someCN2 = GraphUtil.getSomeNeighbor(nyc); 
someCN2.addNeighbor(new City("Berlin")); // Does not compile

ConcreteNode<?> nc = new City("");
nc.addNeighbor(new City("Bern")); // Does not compile

第一种情况有效,因为我知道返回的具体类型,并且它与参数中提供的类型一致。

在第二和第三种情况下,我假设我不知道城市类型。第二种情况有效,但我使用的是原始类型ConcreteNode。

在第三种情况下,第二行出现编译错误:
“类型为TestCity.ConcreteNode的方法addNeighbor(capture#3-of?)不适用于参数(TestCity.City)。”

在该示例中,我使用’new
City(“-”)’作为参数,因为我不知道如何向上转换它们。在第四种情况下,我尝试将City转换为ConcreteNode,但失败了。当前的编译器错误如下:
“类型为TestCity.ConcreteNode的方法addNeighbor(capture#4-of?)不适用于参数(TestCity.City)”

问题:

  1. 如何在不知道城市类型的情况下修复案例2和3?
  2. 如何将City广播到ConcreteNode(或Node)?

谢谢你的帮助。


问题答案:

您可以创建通用 方法 以及通用类型。使用这些,GraphUtils可以像这样解决问题方法:

static <T extends Node<T>> void addNewNeighbors1a(T node, T newNode)
{
  node.addNeighbor(newNode);
  print("Add city to node", node.neighbors());
}

static <T extends Node<T>> void addNewNeighbors2(T node, T newNode)
{
  node.addNeighbor(newNode);
  print("Add concrete node to node", node.neighbors());
}

嘿,等一下…这些是相同的方法!

事实证明,由于它们仅依赖于的接口Node,因此您只需要其中之一即可处理任何Node实现。

在将来,您可能会发现有必要Node像这样更改界面:

public abstract <S extends T> void addNeighbor(S n);


 类似资料:
  • 我试图了解如何将各种递归函数转换为尾递归。我已经查看了许多将斐波那契和阶乘转换为尾递归的示例,并理解了这些示例,但很难跳到具有某种不同结构的问题。一个例子是: 如何将其转换为尾部递归实现? 我已经看过类似的问题,例如:将正常递归转换为尾部递归,但这些似乎并没有转化为这个问题。

  • 在阅读了关于ForkJoinPool的文章之后,我尝试了一个实验,测试与普通递归相比,实际上有多快。 我以递归的方式计算了文件夹中的文件数,令我满意的是,简单的递归比执行得更好 这是我的密码。 递归任务 纯递归 null

  • 我得到了三个整数操作: A-将3添加到number B-将数字 C加倍-交换number 的最后两位数字我应该编写算法来检查我是否可以在n步中使用操作A、B、C制作k素数。最后,我必须打印我用来制作k素数的操作序列。让我们假设我们有函数: 当数字为素数时,函数ifprime返回true,否则返回false。 代码: 我的问题是,我不知道如何记住正确的路径,然后打印出来。

  • 问题内容: 有什么方法可以混合使用递归和语句吗?例如,无限数生成器(使用递归)将类似于: 我试过了: 和 但是他们都没有做我想要的事情,第一个在屈服后停止,第二个在屈服之后,然后是发电机,然后停了下来。 注意: 请知道,您可以使用while循环来做到这一点: 我只想知道这是否可以递归进行。 问题答案: 是的,您可以这样做: 但是,一旦达到最大递归深度,这将出错。 从Python 3.3开始,您将可

  • 请考虑以下类: 注意:很重要的一点是,我不能修改这个类,因为我是从外部API中使用它的。 还要考虑以下订单层次结构: 通过递归地使用(以及一个helper类),我已经设法做到了这一点,如下所示: 这是helper类: 以下一行: 产生以下输出: 到目前为止还不错。结果是绝对正确的。 但是,在阅读了这个问题之后,我对在递归方法中的用法有些担心。特别是,我想知道流是如何被扩展的(如果这是术语的话)。因

  • 我被一个问题缠住好几天了。我的最终目标是在一个通用树上执行预排序、中排序和后排序遍历。我遇到的问题只是填充树。我只能将节点添加到根和根的子节点。我不能通过根的孩子“向下”移动。一天早上,我醒来时想到了一个从下至上的方法递归地构建树的想法。我从来没有用过递归,所以首先,这可能吗?我基本上是通过在树的底部创建节点来构建树,然后再向上工作? 下面是我的节点类: 下面是我的树类: 这是我的主要驱动程序: