2022 CMU15-445 Project0 Trie

news2024/11/27 10:34:20

通过截图

在线测试

image.png

本地测试

image.png

总览

代码风格

我们的代码必须遵循 Google C++ Style Guide。在线检测网站使用 Clang 自动检查源代码的质量。如果我们的提交未通过任何这些检查,您的项目成绩将为零。
对于 Google C++ Style Guide ,我们可以看这里 google-styleguide
对于如何测试,我们可以在 build 目录下,执行以下代码

$ make format
$ make check-lint
$ make check-clang-tidy-p0

以下命令会提示你哪里需要修正,但这不重要,我们先得实现功能,然后再去改这些,这里推荐 vscode 一键格式化代码,把风格设置为 Google,可以避免很多麻烦。

如何测试

test/primer/starter_test.cpp:测试代码
src/include/primer/p0_starter.h:实现代码

本地测试

我们先需要取把测试代码中的 DISABLED_ 前缀去掉,直接查找替换很快的。
在主目录下:

cd build
make starter_trie_test
./test/starter_trie_test

便可看见 project0的本地测试结果

在线测试

登录 https://www.gradescope.com/
注册
入口代码:PXWVR5
学校选 Carnegie Mellon University
还有一些问题可以看这里 https://15445.courses.cs.cmu.edu/fall2022/faq.html
登录进去大概你们就知道怎么测试了,总之在线测试比本地测试更加严格!但是在线测试毕竟没有本地测试那么方便,我们可以根据一些技巧将在线测试集拉下来。
这是这位大佬发出来的:https://blog.csdn.net/freedom1523646952/article/details/122274850

#pragma once
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

void GetTestFileContent() {
  static bool first_enter = true;
  if (first_enter) {
    //  截取gradescope日志输出文件名
    /*
    std::vector<std::string> all_filenames = {
        "/autograder/bustub/test/primer/grading_starter_test.cpp",
        "/autograder/bustub/test/execution/grading_update_executor_test.cpp",
        "/autograder/bustub/test/execution/grading_nested_loop_join_executor_test.cpp",
        "/autograder/bustub/test/execution/grading_limit_executor_test.cpp",
        "/autograder/bustub/test/execution/grading_executor_benchmark_test.cpp",
        "/autograder/bustub/test/concurrency/grading_lock_manager_3_test.cpp",
        "/autograder/bustub/test/buffer/grading_parallel_buffer_pool_manager_test.cpp",
        "/autograder/bustub/test/buffer/grading_lru_replacer_test.cpp",
        "/autograder/bustub/test/execution/grading_executor_integrated_test.cpp",
        "/autograder/bustub/test/execution/grading_sequential_scan_executor_test.cpp",
        "/autograder/bustub/test/concurrency/grading_lock_manager_1_test.cpp",
        "/autograder/bustub/test/execution/grading_distinct_executor_test.cpp",
        "/autograder/bustub/test/buffer/grading_buffer_pool_manager_instance_test.cpp",
        "/autograder/bustub/test/concurrency/grading_lock_manager_2_test.cpp",
        "/autograder/bustub/test/concurrency/grading_transaction_test.cpp",
        "/autograder/bustub/test/buffer/grading_leaderboard_test.cpp",
        "/autograder/bustub/test/container/grading_hash_table_verification_test.cpp",
        "/autograder/bustub/test/concurrency/grading_rollback_test.cpp",
        "/autograder/bustub/test/container/grading_hash_table_concurrent_test.cpp",
        "/autograder/bustub/test/container/grading_hash_table_page_test.cpp",
        "/autograder/bustub/test/concurrency/grading_lock_manager_detection_test.cpp",
        "/autograder/bustub/test/container/grading_hash_table_leaderboard_test.cpp",
        "/autograder/bustub/test/container/grading_hash_table_scale_test.cpp",
        "/autograder/bustub/test/container/grading_hash_table_test.cpp",
        "/autograder/bustub/test/execution/grading_aggregation_executor_test.cpp",
        "/autograder/bustub/test/execution/grading_insert_executor_test.cpp",
        "/autograder/bustub/test/execution/grading_delete_executor_test.cpp",
        "/autograder/bustub/test/execution/grading_hash_join_executor_test.cpp"
        "/autograder/bustub/test/execution/grading_sequential_scan_executor_test.cpp",
        "/autograder/bustub/test/execution/grading_update_executor_test.cpp",
        "/autograder/bustub/test/execution/grading_executor_test_util.h",
        "/autograder/bustub/src/include/execution/plans/mock_scan_plan.h",
        };
    */
    std::vector<std::string> filenames = {
        "/autograder/bustub/test/execution/grading_executor_integrated_test.cpp",
        "/autograder/bustub/test/execution/grading_executor_benchmark_test.cpp",
    };
    std::ifstream fin;
    for (const std::string &filename : filenames) {
      fin.open(filename, std::ios::in);
      if (!fin.is_open()) {
        std::cout << "cannot open the file:" << filename << std::endl;
        continue;
      }
      char buf[200] = {0};
      std::cout << filename << std::endl;
      while (fin.getline(buf, sizeof(buf))) {
        std::cout << buf << std::endl;
      }
      fin.close();
    }
    first_enter = false;
  }
}


我们可以将它插入到我们的测试文件,filenames 写在线测试集文件的位置信息,这个一般在你提交一次后的报错信息中会有,然后就可以得到测试文件了,复制下来替换本地测试文件便可接着 Debug。

Trie

注意事项

Trie 也叫字典树,讲解我主要看的是这个:https://zhuanlan.zhihu.com/p/67431582
官方讲解也很重要:
https://15445.courses.cs.cmu.edu/fall2022/project0/
然后便可以根据每个函数上面的 TODO 提示,进行Coding了。
这里讲几个我碰到的点:

  1. 不需要再添加成员变量了,文件中已经提供了所需要的文件。
  2. vscode 远程开发不会提示报错,所以需要经常执行测试文件进行查看哪里出错。
  3. 当传入参数为指针,且为判断标志的变量,在函数初始时应该初始化为 false,当满足条件的时候才能返回 true
  4. 对 unique_ptr 可以采用 get 成员函数增加美观性
  5. 寻找目标 value 时,不要光看是不是 endNode,也要看 key 是否对应。

代码实现

// ===----------------------------------------------------------------------===//
//
//                          BusTub
//
//  p0_trie.h
//
//  Identification: src/include/primer/p0_trie.h
//
//  Copyright (c) 2015-2022, Carnegie Mellon University Database Group
//
// ===----------------------------------------------------------------------===//

#pragma once

#include <memory>
#include <stack>
#include <stdexcept>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
#include "common/exception.h"
#include "common/rwlatch.h"

namespace bustub {
/**
 * TrieNode is a generic container for any node in Trie.
 */
class TrieNode {
 public:
  /**
   * TODO(P0): Add implementation
   *
   * @brief Construct a new Trie Node object with the given key char.
   * is_end_ flag should be initialized to false in this constructor.
   *
   * @param key_char Key character of this trie node
   */
  explicit TrieNode(char key_char) {
    this->key_char_ = key_char;
    this->is_end_ = false;
    this->children_.clear();
  }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Move constructor for trie node object. The unique pointers stored
   * in children_ should be moved from other_trie_node to new trie node.
   *
   * @param other_trie_node Old trie node.
   */

  TrieNode(TrieNode &&other_trie_node) noexcept {
    this->key_char_ = other_trie_node.key_char_;
    this->is_end_ = other_trie_node.is_end_;
    this->children_.swap(other_trie_node.children_);
  }

  /**
   * @brief Destroy the TrieNode object.
   */
  virtual ~TrieNode() = default;

  /**
   * TODO(P0): Add implementation
   *
   * @brief Whether this trie node has a child node with specified key char.
   *
   * @param key_char Key char of child node.
   * @return True if this trie node has a child with given key, false otherwise.
   */
  bool HasChild(char key_char) const { return children_.find(key_char) != children_.end(); }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Whether this trie node has any children at all. This is useful
   * when implementing 'Remove' functionality.
   *
   * @return True if this trie node has any child node, false if it has no child node.
   */
  bool HasChildren() const { return !children_.empty(); }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Whether this trie node is the ending character of a key string.
   *
   * @return True if is_end_ flag is true, false if is_end_ is false.
   */
  bool IsEndNode() const { return this->is_end_; }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Return key char of this trie node.
   *
   * @return key_char_ of this trie node.
   */
  char GetKeyChar() const { return this->key_char_; }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Insert a child node for this trie node into children_ map, given the key char and
   * unique_ptr of the child node. If specified key_char already exists in children_,
   * return nullptr. If parameter `child`'s key char is different than parameter
   * `key_char`, return nullptr.
   *
   * Note that parameter `child` is rvalue and should be moved when it is
   * inserted into children_map.
   *
   * The return value is a pointer to unique_ptr because pointer to unique_ptr can access the
   * underlying data without taking ownership of the unique_ptr. Further, we can set the return
   * value to nullptr when error occurs.
   *
   * @param key Key of child node
   * @param child Unique pointer created for the child node. This should be added to children_ map.
   * @return Pointer to unique_ptr of the inserted child node. If insertion fails, return nullptr.
   */
  std::unique_ptr<TrieNode> *InsertChildNode(char key_char, std::unique_ptr<TrieNode> &&child) {
    if (HasChild(key_char) || key_char != child->key_char_) {
      return nullptr;
    }
    children_[key_char] = std::forward<std::unique_ptr<TrieNode>>(child);
    return &children_[key_char];
  }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Get the child node given its key char. If child node for given key char does
   * not exist, return nullptr.
   *
   * @param key Key of child node
   * @return Pointer to unique_ptr of the child node, nullptr if child
   *         node does not exist.
   */
  std::unique_ptr<TrieNode> *GetChildNode(char key_char) {
    auto node = children_.find(key_char);
    if (node != children_.end()) {
      return &(node->second);
    }
    return nullptr;
  }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Remove child node from children_ map.
   * If key_char does not exist in children_, return immediately.
   *
   * @param key_char Key char of child node to be removed
   */
  void RemoveChildNode(char key_char) {
    auto node = children_.find(key_char);
    if (node != children_.end()) {
      children_.erase(key_char);
    }
  }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Set the is_end_ flag to true or false.
   *
   * @param is_end Whether this trie node is ending char of a key string
   */
  void SetEndNode(bool is_end) { this->is_end_ = is_end; }

 protected:
  /** Key character of this trie node */
  char key_char_;
  /** whether this node marks the end of a key */
  bool is_end_{false};
  /** A map of all child nodes of this trie node, which can be accessed by each
   * child node's key char. */
  std::unordered_map<char, std::unique_ptr<TrieNode>> children_;
};

/**
 * TrieNodeWithValue is a node that mark the ending of a key, and it can
 * hold a value of any type T.
 */
template <typename T>
class TrieNodeWithValue : public TrieNode {
 private:
  /* Value held by this trie node. */
  T value_;

 public:
  /**
   * TODO(P0): Add implementation
   *
   * @brief Construct a new TrieNodeWithValue object from a TrieNode object and specify its value.
   * This is used when a non-terminal TrieNode is converted to terminal TrieNodeWithValue.
   *
   * The children_ map of TrieNode should be moved to the new TrieNodeWithValue object.
   * Since it contains unique pointers, the first parameter is a rvalue reference.
   *
   * You should:
   * 1) invoke TrieNode's move constructor to move data from TrieNode to
   * TrieNodeWithValue.
   * 2) set value_ member variable of this node to parameter `value`.
   * 3) set is_end_ to true
   *
   * @param trieNode TrieNode whose data is to be moved to TrieNodeWithValue
   * @param value
   */
  TrieNodeWithValue(TrieNode &&trieNode, T value) : TrieNode(std::forward<TrieNode>(trieNode)) {
    this->value_ = value;
    SetEndNode(true);
  }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Construct a new TrieNodeWithValue. This is used when a new terminal node is constructed.
   *
   * You should:
   * 1) Invoke the constructor for TrieNode with given key_char.
   * 2) Set value_ for this node.
   * 3) set is_end_ to true.
   *
   * @param key_char Key char of this node
   * @param value Value of this node
   */
  TrieNodeWithValue(char key_char, T value) : TrieNode(key_char) {
    this->value_ = value;
    SetEndNode(true);
  }

  /**
   * @brief Destroy the Trie Node With Value object
   */
  ~TrieNodeWithValue() override = default;

  /**
   * @brief Get the stored value_.
   *
   * @return Value of type T stored in this node
   */
  T GetValue() const { return value_; }
};

/**
 * Trie is a concurrent key-value store. Each key is string and its corresponding
 * value can be any type.
 */
class Trie {
 private:
  /* Root node of the trie */
  std::unique_ptr<TrieNode> root_;
  /* Read-write lock for the trie */
  ReaderWriterLatch latch_;

 public:
  /**
   * TODO(P0): Add implementation
   *
   * @brief Construct a new Trie object. Initialize the root node with '\0'
   * character.
   */
  Trie() {
    auto *root = new TrieNode('\0');
    root_.reset(root);
  }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Insert key-value pair into the trie.
   *
   * If key is empty string, return false immediately.
   *
   * If key alreadys exists, return false. Duplicated keys are not allowed and
   * you should never overwrite value of an existing key.
   *
   * When you reach the ending character of a key:
   * 1. If TrieNode with this ending character does not exist, create new TrieNodeWithValue
   * and add it to parent node's children_ map.
   * 2. If the terminal node is a TrieNode, then convert it into TrieNodeWithValue by
   * invoking the appropriate constructor.
   * 3. If it is already a TrieNodeWithValue,
   * then insertion fails and return false. Do not overwrite existing data with new data.
   *
   * You can quickly check whether a TrieNode pointer holds TrieNode or TrieNodeWithValue
   * by checking the is_end_ flag. If is_end_ == false, then it points to TrieNode. If
   * is_end_ == true, it points to TrieNodeWithValue.
   *
   * @param key Key used to traverse the trie and find correct node
   * @param value Value to be inserted
   * @return True if insertion succeeds, false if key already exists
   */
  template <typename T>
  bool Insert(const std::string &key, T value) {
    if (key.empty()) {
      return false;
    }
    latch_.WLock();
    auto c = key.begin();
    auto pre_child = &root_;
    while (c != key.end()) {
      auto cur = c++;
      // 若当前节点将为 end 节点,跳出循环进行特殊处理
      if (c == key.end()) {
        break;
      }

      // 如果该字符不存在 则直接创建
      if (!pre_child->get()->HasChild(*cur)) {
        pre_child = pre_child->get()->InsertChildNode(*cur, std::make_unique<TrieNode>(*cur));
      } else {
        // 存在则直接跳过
        pre_child = pre_child->get()->GetChildNode(*cur);
      }
    }

    // 此时c为end 退回一个
    c--;

    auto end_node = pre_child->get()->GetChildNode(*c);
    // 若最后一个节点存在,且已经为 end 则插入失败
    if (end_node != nullptr && end_node->get()->IsEndNode()) {
      latch_.WUnlock();
      return false;
    }
    // 若最后一个节点存在,且不为 end 则转为 TrieNodeWithValue
    if (end_node != nullptr) {
      auto new_node = new TrieNodeWithValue(std::move(**end_node), value);
      end_node->reset(new_node);
      latch_.WUnlock();
      return true;
    }
    //  节点不存在,则直接插入
    pre_child = pre_child->get()->InsertChildNode(*c, std::make_unique<TrieNode>(*c));
    auto new_node = new TrieNodeWithValue(std::move(**pre_child), value);
    pre_child->reset(new_node);
    latch_.WUnlock();
    return true;
  }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Remove key value pair from the trie.
   * This function should also remove nodes that are no longer part of another
   * key. If key is empty or not found, return false.
   *
   * You should:
   * 1) Find the terminal node for the given key.
   * 2) If this terminal node does not have any children, remove it from its
   * parent's children_ map.
   * 3) Recursively remove nodes that have no children and is not terminal node
   * of another key.
   *
   * @param key Key used to traverse the trie and find correct node
   * @return True if key exists and is removed, false otherwise
   */
  bool Remove(const std::string &key) {
    // 为空返回
    if (key.empty()) {
      return false;
    }
    latch_.WLock();
    // 含义为,第二个元素 TrieNode 需要删除 keyChar 等于第一个元素的节点
    std::stack<std::tuple<char, std::unique_ptr<TrieNode> *>> s;
    auto c = key.begin();
    auto pre_child = &root_;
    while (c != key.end()) {
      auto cur = c++;
      if (pre_child->get()->HasChild(*cur)) {
        s.push(std::make_tuple(*cur, pre_child));
        // 到下一个 key
        pre_child = pre_child->get()->GetChildNode(*cur);
        continue;
      }
      // 存在key没有的现象
      latch_.WUnlock();
      return false;
    }

    // 开始删除
    while (!s.empty()) {
      std::tuple<char, std::unique_ptr<TrieNode> *> temp = s.top();
      s.pop();
      auto key = std::get<0>(temp);
      auto del_node = std::get<1>(temp);
      // 若被删除的节点没有孩子,则可以直接删除
      auto flag = (*del_node)->GetChildNode(key);
      if (flag != nullptr && (*flag)->HasChildren()) {
        continue;
      }
      (*del_node)->RemoveChildNode(key);
    }
    latch_.WUnlock();

    return true;
  }

  /**
   * TODO(P0): Add implementation
   *
   * @brief Get the corresponding value of type T given its key.
   * If key is empty, set success to false.
   * If key does not exist in trie, set success to false.
   * If given type T is not the same as the value type stored in TrieNodeWithValue
   * (ie. GetValue<int> is called but terminal node holds std::string),
   * set success to false.
   *
   * To check whether the two types are the same, dynamic_cast
   * the terminal TrieNode to TrieNodeWithValue<T>. If the casted result
   * is not nullptr, then type T is the correct type.
   *
   * @param key Key used to traverse the trie and find correct node
   * @param success Whether GetValue is successful or not
   * @return Value of type T if type matches
   */
  template <typename T>
  T GetValue(const std::string &key, bool *success) {
    // 这个初始化很重要,如果没有找到就直接返回 false,卡这里好久
    *success = false;
    latch_.RLock();

    auto pre_child = &root_;
    auto c = key.begin();
    while (c != key.end()) {
      auto cur = c++;
      auto next_node = pre_child->get()->GetChildNode(*cur);

      if (!next_node) {
        *success = false;
        break;
      }
      // 若是目标尾节点,返回值
      // 这里犯了个错,并非只要是 end 节点就是所需要的节点,key也得对,所以要 key == end 时
      if (next_node->get()->IsEndNode() && c == key.end()) {
        // 若所需要类型和存储的类型不同也失败
        auto flag_node = dynamic_cast<TrieNodeWithValue<T> *>(next_node->get());
        if (!flag_node) {
          *success = false;
          break;
        }
        *success = true;
        latch_.RUnlock();
        return flag_node->GetValue();
      }
      // 切换到下个
      pre_child = next_node;
    }
    latch_.RUnlock();
    return {};
  }
};
}  //  namespace bustub

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/51677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot Logback启动流程

Spring Boot 默认使用的是 Logback 的日志框架、Logback 的组件主要通过 Spring Boot ApplicationListener 启动的 // LoggingApplicationListener Override public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationStartingEvent) {onApp…

测试工作中的测试用例设计

测试工作中的测试用例设计 测试工作的最核心的模块&#xff0c;在执行任何测试之前&#xff0c;首先必须完成测试用例的编写。测试用例是指导你执行测试&#xff0c;帮助证明软件功能或发现软件缺陷的一种说明。 进行用例设计&#xff0c;需要对项目的需求有清晰的了解&#xf…

Excel找回打开密码过程

Excel文件设置了打开密码&#xff0c;但是忘记了打开密码或者不知道这份文件的打开密码都没办法打开excel文件了。可是文件的打开密码&#xff0c;一旦忘记了&#xff0c;想要再打开文件&#xff0c;都是需要找回密码的。网上的一些绕过密码、直接删除密码都是无效的解决方法。…

C# 文件压缩解压与sqlite存储文件数据

文章目录环境压缩nugetUI代码资源链接&#xff08;下载地址&#xff09;ZipFile 类方法环境 .netframerwork4.8sqlite3 压缩 nuget <package id"System.IO.Compression" version"4.3.0" targetFramework"net48" /><package id"…

四嗪-五聚乙二醇-羧基,1682653-79-7,Tetrazine-PEG5-COOH 水溶性和稳定性怎么样?

●中文名&#xff1a;四嗪-五聚乙二醇-羧基 ●英文&#xff1a;Tetrazine-PEG5-COOH ●外观以及性质&#xff1a;Tetrazine-PEG5-COOH为红色固体&#xff0c;四嗪目前被广泛应用于蛋白质特定位点功能阐释、亚细胞结构选择性标记。四嗪PEG衍生物用于与 TCO&#xff08;反式环辛烯…

【Linux初阶】操作系统概念与定位 | 操作系统管理硬件方法、系统调用和库函数概念

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;操作系统的基础概念、设计OS的目的&#xff0c;操作系统的定位&#xff0c;操作系统管理硬件方法&#xff0c;…

【正厚软件干货】我推荐你的入门编程语言选python

By——正厚技术极客陈多多 当友友看到这篇文章的时候&#xff0c;心里一定有一个学习编程的想法&#xff0c;但是又不知道挑选哪个作为入门语言&#xff01;我写这篇文章就是为了帮有困难的你做出选择&#xff01;&#xff08;作者本人有选择困难症&#xff0c;当时也纠结了好久…

图神经网络关系抽取论文阅读笔记(五)

1 依赖驱动的注意力图卷积网络关系抽取方法&#xff08;Dependency-driven Relation Extractionwith Attentive Graph Convolutional Networks&#xff09; 论文&#xff1a;Dependency-driven Relation Extraction with Attentive Graph Convolutional Networks.ACL 2021 1.1 …

Win11系统禁止关机键关机的方法教学

Win11系统禁止关机键关机的方法教学。在操作电脑的时候&#xff0c;有用户经常出现自己误触关键按键导致电脑关机的情况。对于这个情况&#xff0c;我们可以去开启电脑禁止关机按键关机的设置。这样就可以不用担心误触导致关机的问题了。一起看看设置的方法吧。 操作方法&#…

OFFER狂魔成长指南

OFFER OFFER狂魔成长指南 前言 本文章是总结了我春招的经历&#xff0c;博主背景为211某流计算机专业&#xff0c;考研失败转春招。因为计划会再考一年因此主要面试的偏国企专业&#xff08;现在放弃了&#xff0c;因为公司里面校招生就我一个本科&#xff0c;其他都是21198…

基于VDI2230规范的螺栓评估(上)

作者&#xff1a;王庆艳&#xff0c;安世中德工程师&#xff0c;仿真秀科普作者 一、写在前面 【螺栓评估准则】&#xff1a;VDI2230&#xff08;高强度螺栓连接系统计算&#xff09;规范是德国工程师协会负责编写整理&#xff0c;包括Part1及Part2两部分&#xff0c;是目前针…

(5)点云数据处理学习——其它官网例子2

1、主要参考 &#xff08;1&#xff09;官方稳定地址 Point cloud — Open3D 0.16.0 documentation 2、相关功能 2.1凸包&#xff08;Convex hull&#xff09; &#xff08;1&#xff09;函数 compute_vertex_normals create_from_triangle_mesh &#xff08;2&#xff09;…

Three.js教程之在网页快速实现 3D效果(教程含源码)

介绍 本文概述了与使用 Three.js 在常规 Web 浏览器中直接在 Web 上制作 3D 图形相关的术语和概念。对于 3D,就像任何主题一样,如果您深入了解所有细节,事情会很快变得复杂。我将尝试做相反的事情,并简单概述您在学习如何在常规 Web 浏览器中制作 3D 时会遇到的所有概念。 …

手把手教你用站长工具综查询网站域名在各个平台的权重情况 站长工具综查询

网站权重是根据流量值来判断网站的权重值。权重值是属于第三方评估就有参考的意义。 了解网站的权重可以助于SEO专员找到工作的方向。使用站长工具综合查询可以看到网站在各个平台的权重情况。 用站长工具综合查询网站在各个平台的权重情况的操作步骤吧&#xff01; 第一步、打…

德云一哥岳云鹏,准备录制河南和东方卫视节目,央视春晚还参加吗

时间如白驹过隙&#xff0c;转眼马上又要过年了&#xff0c;央视春晚也备受关注&#xff0c;不知道今年春晚究竟有哪些明星登台演出。说起央视春晚舞台&#xff0c;就不得不说起相声和小品&#xff0c;这两个节目是春晚的台柱子&#xff0c;也一直深受大家的喜爱。 尤其是最近几…

Linux基础概念,目录文件操作命令,压缩命令:

Linux基础概念&#xff1a; 1&#xff0c;linux登录方式&#xff0c;本地登录&#xff0c;远程登录&#xff08;借助xshell等&#xff09; 登录后是shell交互界面&#xff0c;用c编写的程序。常见的就是bash&#xff0c;sh&#xff0c;csh&#xff0c;ksh等。 2&#xff0c;li…

项目实战——Web自动化测试

目录 一、前言及测试用例设计 二、 首页测试&#xff08;未登录&#xff09; 三、注册测试 四、对局列表测试 五、排行榜测试 六、对战测试 七、Bot测试 八、测试套件Suite 一、前言及测试用例设计 整个项目已经部署完成&#xff0c;我们历经九九八十一难&#xff0c;…

英国Top20名校更偏爱IB申请党?

IB的全球超高认可度相信大家都有共识&#xff0c;英国大学对于A-Level、IB成绩都持认可态度。 但多数人会认为A-Level成绩申请英国名校会比IB更有优势&#xff0c;事实果真如此吗&#xff1f;接下来通过官方给出的数据&#xff0c;我们来一探究竟~ 01英国Top20名校更偏爱IB学生…

Linux的十个常用命令

目录 1、ls 2、pwd 3、cd 4、touch 5、cat 6、echo 7、mkdir 8、rm 9、mv 10、cp 1、ls ls命令用于显示目录中的文件信息. 格式&#xff1a;ls [选项] [文件] 参数&#xff1a; -a 显示所有文件及目录 (. 开头的隐藏文件也会列出)-l 除文件名称外&#xff0c;亦将文件型…

点云缩放(附open3d python代码)

1/ numpy 数组方法 通过将点云数组乘以一个缩放因子来改变大小, 同时通过加法运算实现质心平移。points = points/2.0#缩小到原来的一半 points[:, 0] = points[:, 0] + 20#质心平移到x=20处2/ open3d的缩放函数为scale,包含两个参数。 第一个参数是缩放的比例,即放大的倍数…