ADT and OOP

  • 1 实验目标概述
  • 2 实验环境配置
  • 3 实验过程
    • 3.1 Poetic Walks
      • 3.1.1 Get the code and prepare Git repository
      • 3.1.2 Problem 1: Test Graph
      • 3.1.3 Problem 2: Implement Graph
        • 3.1.3.1 Implement ConcreteEdgesGraph
        • 3.1.3.2 Implement ConcreteVerticesGraph
      • 3.1.4 Problem 3: Implement generic Graph
        • 3.1.4.1 Make the implementations generic
        • 3.1.4.2 Implement Graph.empty()
      • 3.1.5 Problem 4: Poetic walks
        • 3.1.5.1 Test GraphPoet
        • 3.1.5.2 Implement GraphPoet
        • 3.1.5.3 Graph poetry slam
      • 3.1.6 Before you’re done
    • 3.2 Re-implement the Social Network in Lab1
      • 3.2.1 FriendshipGraph类
      • 3.2.2 Person类
      • 3.2.3 客户端main()
      • 3.2.4 测试用例
      • 3.2.5 提交至Git仓库
    • 3.3 Playing Chess
      • 3.3.1 ADT设计/实现方案
      • 3.3.2 主程序ChessGame设计/实现方案
      • 3.3.3 ADT和主程序的测试方案
    • 3.4 Multi-Startup Set (MIT)
  • 4 实验进度记录
  • 5 实验过程中遇到的困难与解决途径

1 实验目标概述

本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象编程(OOP)技术实现 ADT。具体来说:
⚫ 针对给定的应用问题,从问题描述中识别所需的 ADT;
⚫ 设计 ADT 规约(pre-condition、post-condition)并评估规约的质量;
⚫ 根据 ADT 的规约设计测试用例;
⚫ ADT 的泛型化;
⚫ 根据规约设计 ADT 的多种不同的实现;针对每种实现,设计其表示(representation)、表示不变性(rep invariant)、抽象过程(abstraction function)
⚫ 使用 OOP 实现 ADT,并判定表示不变性是否违反、各实现是否存在表示泄露(rep exposure);
⚫ 测试 ADT 的实现并评估测试的覆盖度;
⚫ 使用 ADT 及其实现,为应用问题开发程序;
⚫ 在测试代码中,能够写出 testing strategy 并据此设计测试用例。

2 实验环境配置

延续Lab1中的实验环境,除此之外本次实验需要在 Eclipse IDE 中安装配置 EclEmma(一个用于统计 JUnit 测试用例的代码覆盖度的 plugin)。
建立自己的 Lab2 仓库并关联至自己的学号:

3 实验过程

3.1 Poetic Walks

3.1主要完成一个图的模块并基于该模块进行使用,只需修改graph中的构造函数。
1.对给定的接口Graph,完成两个实例类ConcretVerticesGraph和ConcreateEgdesGraph:
①分别构造ConcretVerticesGraph和ConcreateEgdesGraph的Vertex和Edge
②用String作为L的特例,分别实现Graph接口中的方法,写test文件测试正确性
③将所有String变量替换为泛型L
2.对给定的文本文件建立有向图,调用Graph作为存储的数据结构,利用该结构对输入的字符串进行扩充。

3.1.1 Get the code and prepare Git repository

如何从GitHub获取该任务的代码、在本地创建git仓库、使用git管理本地开发。

  1. 获取任务代码
    Download ZIP获取任务代码 或 使用
    git clone https://github.com/rainywang/Spring2020_HITCS_SC_Lab2.git
  2. 管理本地开发
    git init //生成.git目录
    git add * //该目录下所有文件纳入git管理【文件->暂存区】
    git status //具体信息
    git commit -m “v0.1 All code in main()” //创建待提交的数据结构【暂存区->本地仓库】
    git log //查看提交信息
    git remote add origin “仓库地址”
    git push -u -f origin master【本地仓库->线上仓库】

3.1.2 Problem 1: Test Graph

方法 实现
private void checkRep() 判断点的数目n和边的数目m关系:m<=0.5n(n-1)
public boolean add(String vertex) 判断参数vertex是否存在vertices中,存在则返回false;否则将参数vertex加入点集vertices中,返回true
public int set(String source, String target, int weight) 增加、修改或删除边(的权值)。先判断weight是否小于0。若是,直接返回-1;若不是,判断是否为0。若是,检查以source和target为两端点的边,若存在则删除边,没有则抛出异常;若不为0,检查该边是否已存在,若已存在则修改边的权值为weight。
public boolean remove(String vertex) 判断vertices中是否有传入的参数vertex,若不存在则返回false;若存在则先后序遍历edges,删除与该点有关的所有边,再删除该点。
public Set vertices() 使用Collections.unmodifiableSet将vertices复制到新的Set中,返回这个Set(注意Safety from rep exposure)
public Map<String, Integer> sources(String target) 建立map,遍历edges,若某个edge.getTarget和传入的参数target相等,将该edge的source和weight存入map中。返回map
public Map<String, Integer> targets(String source) 建立map,遍历edges,若某个edge.getSource和传入的参数source相等,将该edge的target和weight存入map中。返回map

3.1.3 Problem 2: Implement Graph

  1. checkRep要求边数m与点数n满足:m<=0.5n(n-1)
  2. 实现抽象类,重写函数
  3. Edge类
作用
private final L source 边的起点
private final L target 边的终点
private final int weight 边的权值
方法 作用
public L getSource() Getter
public L getTarget() Getter
public int getWeight() Getter
public void setSource(L source) Setter
public void setTarget(L target) Setter
public void setWeight(int weight) Setter
  1. ConcreteEdgesGraph
方法 实现
boolean add(String vertex) 判断参数vertex是否存在vertices中,存在则返回false;否则将参数vertex加入点集vertices中,返回true
public int set(String source, String target, int weight) 增加、修改或删除边(的权值)。先判断weight是否小于0。若是,直接返回-1;若不是,判断是否为0。若是,检查以source和target为两端点的边,若存在则删除边,没有则抛出异常;若不为0,检查该边是否已存在,若已存在则修改边的权值为weight。
public boolean remove(String vertex) 判断vertices中是否有传入的参数vertex,若不存在则返回false;若存在则先后序遍历edges,删除与该点有关的所有边,再删除该点。
public Set vertices() 使用Collections.unmodifiableSet将vertices复制到新的Set中,返回这个Set(注意Safety from rep exposure)
public Map<String, Integer> sources(String target) 建立map,遍历edges,若某个edge.getTarget和传入的参数target相等,将该edge的source和weight存入map中。返回map
public Map<String, Integer> targets(String source) 建立map,遍历edges,若某个edge.getSource和传入的参数source相等,将该edge的target和weight存入map中。返回map

3.1.3.1 Implement ConcreteEdgesGraph

3.1.3.2 Implement ConcreteVerticesGraph

  1. Vertex类
  • fields
    private String name;//标志量
    private Map<String, Integer> relationMap = new HashMap<>();

  • [ ]
    // Abstraction function:
    // 使用HashMap存取映射关系
    // 存取顶点vertex以及一个记录所有边的map

    // Representation invariant:
    // 每个顶点的source或target不能是自身
    // HashMap中的weight必须不小于0

    // Safety from rep exposure:
    // 所有fields是private final
    // String是imutable类型

  • constructor
    public Vertex(String name) {
    this.name = name;
    checkRep();
    }

    public String getSource() {
    // TODO Auto-generated method stub
    return this.name;
    }

  • function

方法 作用
checkRep() 每条边的权值不能为负
getWeight(String target) 得到权重
deleteEdge(String target) 删除边
addEdge(String target, int weight) 加边
Map<String, Integer> getRelationMap() 获取关系图
toString() 转化为字符串形式
setRelationMap(Map<String, Integer> relationMap) setter
  1. ConcreteVerticesGraph类
  • rep
    private final List<Vertex> vertices = new ArrayList<>();
  • // Abstraction function:
    // TODO 顶点AF为有target和对应映射weight的有向加权图
    // Representation invariant:
    // TODO weight>0
    // Safety from rep exposure:
    // TODO private final (field)
  • function
Column 1 Column 2
void checkRep() 权重不为负
boolean add(String vertex) 遍历,若不存在相同点则加入顶点vertex,返回true;否则返回false
int set(String source, String target, int weight) 为该图加入一条带source、target和权重weight的边。Source在原图有无需分类,若无则先把source加入图中;加入/或原图已有边后,权重是否为0也需分类,若为0则删除该边,返回权值
boolean remove(String vertex) 删除与该顶点vertex有关的边的点,最后删除该顶点
Set vertices() 将所有的顶点加入一个新的HashSet
Map<String, Integer> sources(String target) 返回sourceMap
Map<String, Integer> targets(String source) 返回targetMap
String toString() 转化为字符串形式

3.1.4 Problem 3: Implement generic Graph

3.1.4.1 Make the implementations generic

将两个实例类中的所有String类的参数替换为泛型的参数(声明、函数参数、返回值、rep)

3.1.4.2 Implement Graph.empty()

3.1.5 Problem 4: Poetic walks

3.1.5.1 Test GraphPoet

方法
centered 文本居中
语料库文件配置为:“test/P1/poet/one_line.txt”
文本为:This is a test of the Mugar Omni Theater sound system.
完整代码如下:

@Test public void testGraphPoet() throws IOException {
final GraphPoet nimoy = new GraphPoet(new File(“src/P1/poet/mugar-omni-theater.txt”));
final String input = “Test the system.”;
assertEquals(“Test of the system.”,nimoy.poem(input));
}|

3.1.5.2 Implement GraphPoet

  1. fields
    private final Graph graph = Graph.empty();
    private Map<String, Integer> targetMap;
    private Map<String, Integer> sourceMap;
    private Map<String, Integer> chosenMap = new HashMap<>();

  2. // Abstraction function:
    // TODO 边图
    // Representation invariant:
    // TODO 图的顶点为非空字符串
    // Safety from rep exposure:
    // TODO private final (field).

  3. constructor

public GraphPoet(File corpus) throws IOException 读入文件;按空格分割单词存入字符串数组words中;由于读入不分大小写,因此调用toLowerCase()将字符全部转化为小写;按边的出现次数设置每个单词的权重;checkRep();
  1. function
方法 作用
String toString() 转化为字符串
void checkRep() 桥接词转换为小写;顶点非空
String poem(String input) 分割句子;找到所有桥接单词加入chosenMap;找到权值最大的桥接单词;去掉空白字符,加入桥接词;toString输出

3.1.5.3 Graph poetry slam

Test自定义文本进行测试
// Testing strategy
// 给定一个input.从文件中读取poet
// 调用Graph.poem后观察输出与预期是否相等
// 一个单词、一行单词

3.1.6 Before you’re done

在这里给出你的项目的目录结构树状示意图。

3.2 Re-implement the Social Network in Lab1

继承P1中ConcreteEdgesGraph或者ConcreteVerticesGraph类,实现FriendshipGraph.java中 addVertex、addEdge、getDistances三个接口

3.2.1 FriendshipGraph类

方法 作用
void addVertex(Person person) 要点:加入person前检查该person是否已存在
void addEdge(Person p1, Person p2) 设置两人之间的关系
int getDistance(Person p1, Person p2) 主要用队列方法实现import java.util.Queue;首先判断p1,p2是否为同一个person;若不是,用队列寻找与p1有关系的person,队列不空时循环,找到第一个与该person有关系的person,循环直到无人与他有关系——直到找到p2;若没有找到p2,则两人无关系
static void main(String[] args) 主函数无返回

3.2.2 Person类

private String name;
private static Set nameSet = new HashSet<>();

方法 作用
Person(String name) 判断person是否已存在
String getName() 返回该人的名字

3.2.3 客户端main()

3.2.4 测试用例

3.2.5 提交至Git仓库

git add * //该目录下所有文件纳入git管理【文件->暂存区】
git status //具体信息
git commit -m “v0.1 All code in main()” //创建待提交的数据结构【暂存区->本地仓库】
git log //查看提交信息
git remote add origin “仓库地址”
git push -u -f origin master【本地仓库->线上仓库】

3.3 Playing Chess

3.3.1 ADT设计/实现方案

  1. Position
  • fields
    private int x;
    private int y;
  • function
Column 1 Column 2
getter
setter public void setPos(int x, int y) {this.x = x;this.y = y;} 设置坐标时直接设置x,y
boolean posEqual(Position pos) 判断x和y是否与pos的x和y相等,若相等返回true
String toString() 转化为字符串,格式为(x, y)
boolean checkPos(int type) 选择下棋模式,1为国际象棋,2为围棋
  1. Player
  • fields
    private String playerName;
  • function
方法 作用
Player(String playerName) setter
String getPlayerName() getter
  1. Piece
  • fields
    private Position piecePos = new Position();//棋子位置
    private String pieceName = new String();//棋子名字
    private boolean player;//棋子拥有者,true为棋手1,false为棋手2
  • function
方法 作用
Piece(int x, int y, String pieceName, boolean player) 初始化棋子信息(坐标、拥有者、名字)
void setPiecePos(int x, int y) setter
void setPlayer(boolean player)
void setPieceName(String pieceName)
Position getPiecePos() getter
boolean getPlayer()
String getPieceName()
  1. Board
  • fields
    //每种下棋模式设置两个bollean二维数组,一个存储占用情况,一个存储棋手
    private boolean[][] goboard_taken = new boolean[19][19];//true:占用
    private boolean[][] goboard_player = new boolean[19][19];//true:player1.
    private boolean[][] chessboard_taken = new boolean[8][8];
    private boolean[][] chessboard_player = new boolean[8][8];
  • function
方法 作用
void Go_setPiece(Piece piece) 落子操作【围棋】
void Chess_deletePiece(Piece piece) 删除操纵【象棋/围棋】
void Chess_movePiece(Piece piece) 移子操作【象棋】
boolean getOccupationOrNot(Position pos, int type) 判断棋盘某位置是否被占用,有关goboard_taken和chessboard_taken
boolean getOccupationPlayer(Position pos, int type) 判断棋盘某位置被谁占用,有关c goboard_player和hessboard_player
void checkOccupation(Position pos, int type) 检查占用情况
Board(int type) 初始化棋盘(象棋初始有棋手1和棋手2对称放置的棋子;围棋初始无棋子——在对应位置设置taken数组和player数组的初始boolean情况)
  1. Action
    检查移子和吃子坐标是否合理
  • 移子
    需检查(当前位置和目标位置)是否超出棋盘范围【超出返回false】,当前位置是否有棋子【无棋子返回false】,当前位置是否为己方棋子【否返回false】,目标位置是否有棋子【有返回false】,其余返回true。
  • 吃子
    需检查(当前位置和目标位置)是否超出棋盘范围【超出返回false】,当前位置是否有棋子【无棋子返回false】,当前位置是否为己方棋子【否返回false】,目标位置是否有棋子【无返回false】,目标位置是否为敌方棋子【否返回false】,其余返回true。
  1. Game
  • fields
    private Map<String, String> map = new HashMap<String, String>();//(英,中)
    private static Board chessBoard = new Board(1);//象棋棋盘
    private static Board goBoard = new Board(2);//围棋棋盘
    private Player player1;
    private Player player2;
    private static List Pieces1 = new ArrayList<>();
    private static List Pieces2 = new ArrayList<>();
  • function
方法 作用
void ChessMenu() 象棋模式
void GoMenu() 围棋模式
boolean putPiece(int x, int y, boolean player) 围棋落子。首先检查落子合法性,接着调用Piece类给定落子的玩家
boolean checkPutPiece(int x, int y) 检查落子合法性(当前位置是否有棋子、是否超出棋盘范围)
boolean extractPiece(int x, int y, boolean player) 围棋提子。首先检查提子合法性,调用Piece类给定提子的玩家。原则:己方提子,对方少子
boolean checkExtracPiece(int x, int y, boolean player) 检验提子合法性(当前位置是否有棋子、当前位置是否为敌方棋子、是否超出棋盘范围)
boolean movePiece(int x, int y, int xNew, int yNew, boolean player) 象棋移子。子类Piece getPiece(Position pos)
Piece getPiece(Position pos) 返回要移动的棋子。子类int getPiecesSize(boolean player)
int getPiecesSize(boolean player) 返回移动棋子的棋手。
boolean eatPiece(int x, int y, int xNew, int yNew, boolean player) 象棋吃子。首先检验吃子合法性,吃子包括两步(删除目标位置敌方棋子,移动己方棋子到目标位置上)
public void setPlayer1(String name) setter
public String getPlayer1() getter
Board getBoard(int type) 选择棋盘模式。1为象棋,2为围棋
void printBoard(int type) 输出当前棋盘状态
Game(int type)

3.3.2 主程序ChessGame设计/实现方案

public static void main(String[]args) {Scanner input = new Scanner(System.in);int player = 0;System.out.println("选择模式(chess or go):");String type = input.nextLine();int choice;if(type.equals("chess")) {Game game = new Game(1);System.out.print("输入棋手1的名字");String player1 = input.nextLine();game.setPlayer1(player1);System.out.print("输入棋手2的名字");String player2 = input.nextLine();game.setPlayer2(player2);game.ChessMenu();choice = input.nextInt();while(true) {switch(choice) {case 1:System.out.println("请输入移子坐标");if(game.movePiece(input.nextInt(),input.nextInt(),input.nextInt(),input.nextInt(),player % 2 == 0)) {//这一步用于交换主动权player++;}game.printBoard(1);break;case 2:System.out.println("请输入吃子坐标");if(game.eatPiece(input.nextInt(),input.nextInt(),input.nextInt(),input.nextInt(),player % 2 == 0)) {//这一步用于交换主动权player++;}game.printBoard(1);break;case 3:input.close();System.exit(0);case 4:game.printBoard(1);break;case 5:System.out.println("请输入d待查询的坐标");Position pos = new Position();pos.setPos(input.nextInt(), input.nextInt());game.getBoard(1).checkOccupation(pos, 1);break;default:System.out.println("请重新输入!");}//循环game.ChessMenu();choice = input.nextInt();}}else if(type.equals("go")) {Game game = new Game(2);System.out.print("输入棋手1的名字");String player1 = input.nextLine();game.setPlayer1(player1);System.out.print("输入棋手2的名字");String player2 = input.nextLine();game.setPlayer2(player2);game.GoMenu();choice = input.nextInt();while(true) {switch(choice) {case 1:System.out.println("请输入落子坐标");if(game.putPiece(input.nextInt(),input.nextInt(),player % 2 == 0)) {//这一步用于交换主动权player++;}game.printBoard(2);break;case 2:System.out.println("请输入提子坐标");if(game.extractPiece(input.nextInt(),input.nextInt(),player % 2 == 0)) {//这一步用于交换主动权player++;}game.printBoard(1);break;case 3:input.close();System.exit(0);case 4:game.printBoard(1);break;case 5:System.out.println("请输入d待查询的坐标");Position pos = new Position();pos.setPos(input.nextInt(), input.nextInt());game.getBoard(1).checkOccupation(pos, 1);break;default:System.out.println("请重新输入!");}//循环game.ChessMenu();choice = input.nextInt();}}else {System.out.println("MODE ERROR!");input.close();System.exit(0);}input.close();}

3.3.3 ADT和主程序的测试方案

  1. public class BoardTest
    删除棋子后,检验棋子在当前位置的占用情况;再次添加棋子,检验占用情况
  2. public class ClassTest
    1)检验移子testMovePiece()
    是否越界、是否移动对方棋子、目标位置是否已被占用
    2)检验吃子testEatPiece()
    是否越界、目标棋子是否为对方棋子、起始棋子是否为己方棋子
    3)检验落子testCheckPlacePiece()
    是否越界、目标位置是否被占用
    4)检验提子tetsCheckExtracPiece()
    是否越界、目标位置是否为对方棋子

3.4 Multi-Startup Set (MIT)

4 实验进度记录

日期 时间段 计划任务 实际完成情况
4.6 13:00~18:00 P1 Test Graph P1中ConcreteEdgesGraph.java及其测试文件ConcreteEdgesGraphTest.java、Graph.java
4.7 16:00~20:00 P1 ConcreteVerticesGraph 未完成
4.7 22:00~24:00 P1 ConcreteVerticesGraph、P2 完成P1、P2.Person
4.8 18:00~22:00 P2 完成
4.9 13:00~18:00 理解P3,设定基本需要完成的类 完成v1.0,但无法运行,重新设计
4.9 24:00~? 重新设计P3 在小组讨论情况下决定了Action、Board、Game、Piece、Player、Position几个类
4.10 9:00~11:30 P3 完成Player、Position、Piece、Board几个较简单的类的设计
4.10 15:00~18:00 P3 完成Action、开始设计Game
4.11 8:30~11:30 P3 重新设计Position和Action,完成Game
4.11 18:00~24:00 P3 完成
4.12 / / 修改部分代码完善功能

5 实验过程中遇到的困难与解决途径

Abstraction function
Representation invariant
rep

寻找同学解释、回顾课上内容
“AF是抽象函数
RI是表示不变量
Rep是你内部的表示
让你在TODO那行把你的ADT相应的内容填上”

Lab1实验时不能太理解this的用法。Lab2用起来吃力

寻找同学解释,CSDN查询

P2有关泛型类独自无法解决

小组讨论、CSDN查询、JDK1.6 帮助文档

在设计P3时,单纯的Set或Map解决问题有些繁琐,工程量较大

通过查询JDK1.6 帮助文档,发现可以使用队列Queue解决问题

2020春软件构造Lab2 ADT and OOP相关推荐

  1. 哈工大2020春软件构造实验二实验报告

    2020年春季学期 计算机学院<软件构造>课程 Lab 2实验报告 姓名 赵俊 学号 1180300508 班号 1836101 电子邮件 手机号码 目录 1 实验目标概述 1 2 实验环 ...

  2. [HITSC]哈工大2020春软件构造Lab3实验报告

    Github地址 1 实验目标概述 本次实验覆盖课程第 3.4.5 章的内容,目标是编写具有可复用性和可维护 性的软件,主要使用以下软件构造技术: 子类型.泛型.多态.重写.重载 继承.代理.组合 常 ...

  3. 哈工大2020软件构造Lab2 Problem3 Playing Chess 架构设计思路

    哈工大2020春软件构造实验2 Problem 3 Playing Chess 架构设计思路 问题简述 整体结构 ADT功能设计 功能实现路径 问题简述: 设计一款棋类游戏,同时支持国际象棋(Ches ...

  4. 哈工大2021春软件构造实验

    2021春软件构造(Software Construction)课程共3个实验,其中lab1和lab2同往年一样,lab3是全新的实验. 3个实验的内容如下: Lab-1: Fundamental J ...

  5. 软件构造lab2 - 实验报告

    软件构造lab2 - 实验报告 1.实验目标概述 2.环境配置 3.实验过程 3.1Poetic Walks 3.1.1Get the code and prepare Git repository ...

  6. 哈工大2021春软件构造实验总结

    哈工大2021春软件构造实验总结 文章目录 一.实验一 1. 实验概述 1.1 Magic Squares 1.2 Turtle Graphics 1.3 Social Network 2. 实验感受 ...

  7. 哈工大18年春软件构造课程讨论题

    这是哈工大18年春软件构造课程(徐汉川老师)的讨论题目,少部分答案摘录自课件PPT和网上的资源(链接在文中给出).如有错误还望指出,谢谢. 一.在软件测试过程中,"测试用例的数目" ...

  8. 哈工大2020软件构造Lab2实验报告

    本项目于3.17日实验课验收,请放心参考 参考时文中有给出一些建议,请查看 基本更新完成 2020春计算机学院<软件构造>课程Lab2实验报告 Software Construction ...

  9. 软件构造Lab2总结

    2020年春季学期 计算机学院<软件构造>课程 Lab2实验报告 ·· 1 3.1 Poetic Walks· 1 3.1.1 Get the code and prepare Git r ...

最新文章

  1. FZU 1686 神龙的难题(DLX反复覆盖)
  2. eclipse + android 自动补全
  3. mysql删除了密码怎样恢复_window 下如何恢复被删除的mysql root账户及密码(mysql 8.0.17)...
  4. linux的mysql主主_Linux下指定mysql数据库数据配置主主同步的实例
  5. python编写一个登陆验证程序_python项目实战:实现验证码登录网址实例
  6. 低成本、高性能创客开发板——PYB Nano
  7. 关于Unity中坐标系的种类
  8. 京东方计划为苹果iPhone 13供应6.06英寸OLED面板
  9. 树莓派安装qq linux,在(Raspberry Pi)树莓派上安装NodeJS
  10. Java书籍推荐(这些书你看过几本?)
  11. Could not get a resource since the pool is exhausted
  12. 自由技艺 (Liberal arts)
  13. 要么听我的,要么走开(摘自《代码之道》第8章)
  14. vsftpd cmds_allowed 权限控制
  15. 云桌面有哪些优势-为什么企业使用云桌面是趋势
  16. matplotlib+basemap画出标记地图
  17. 在 Mac 上通过“启动转换助理”安装 Windows 10
  18. 微信小程token_微信小程序登录换取token
  19. ABC166E This Message Will Self-Destruct in 5s 题解
  20. win10 蓝牙忽然消失 华硕主板

热门文章

  1. 侵入式与非侵入式链表
  2. 华为 HCIA-AI V3.0 认证人工智能工程师考试
  3. PIC单片机延时问题
  4. 舌尖上的职场(二)一起去吃饭吧!(转)
  5. python3 地下城堡2计算资源何时满仓
  6. 专访胡润:中国富豪正变得透明
  7. Electron,打造上班划水摸鱼桌面小工具:“股票监控“软件
  8. java 根据ip地址获取地理位置及运营商。
  9. 冯杰的手写艺术签名怎么写好看
  10. android 使用百度全景sdk出现的问题